How to Translate SQL "WHERE expr IN (query)" into LINQ? - c#

Basically I want to make this SQL query with linq:
SELECT *
FROM Orders
WHERE Identifier IN (SELECT DISTINCT [Order] FROM OrderRows WHERE Quantity = '1')
This is what I have come up with:
var q = from o in db.Orders
where o.Identifier in (from r in db.OrderRows
where r.Quantity == 1 select r.Order).Distinct());
But the in after o.Identifier is not valid.
What is the correct syntax for the keyword IN?

I'm a little late, but I made a demo!
As other people have stated, I always use Contains:
using System;
using System.Collections.Generic;
using System.Linq;
namespace ContainsExample
{
class Program
{
static void Main(string[] args)
{
var foos = new List<Foo>
{
new Foo { ID = 1, FooName = "Light Side" },
new Foo { ID = 2, FooName = "Dark Side" }
};
var bars = new List<Bar>
{
new Bar { ID = 1, BarName = "Luke", FooID = 1 },
new Bar { ID = 2, BarName = "Han", FooID = 1 },
new Bar { ID = 3, BarName = "Obi-Wan", FooID = 1 },
new Bar { ID = 4, BarName = "Vader", FooID = 2 },
new Bar { ID = 5, BarName = "Palpatine", FooID = 2 },
new Bar { ID = 6, BarName = "Fett", FooID = 2 },
new Bar { ID = 7, BarName = "JarJar", FooID = 3 }
};
var criteria = from f in foos
select f.ID;
var query = from b in bars
where criteria.Contains(b.FooID)
select b;
foreach (Bar b in query)
{
Console.WriteLine(b.BarName);
}
Console.WriteLine();
Console.WriteLine("There should be no JarJar...");
Console.ReadLine();
}
}
public class Foo
{
public int ID { get; set; }
public string FooName { get; set; }
}
public class Bar
{
public int ID { get; set; }
public string BarName { get; set; }
public int FooID { get; set; }
}
}

from o in db.Orders
where o.Identifier.Any
(
from r in db.OrderRows
where r.Quantity == 1
select r.Order
).Distinct()
select o
Try this...

It seems like you want a join:
var q = (from o in db.Orders
join r in db.OrderRows on o.Identifier equals r.Order
where r.Quantity == 1
select o).Distinct();

var q = from o in db.Orders
where (from r in db.OrderRows
where r.Quantity == 1 select r.Order).Distinct().Contains(o.Identifier);

The short answer is you want to take advantage of the Contains method.
int[] ids = { 2, 5, 6, 1 };
var a = from myRecords in context.db
where ids.Contains (myRecords.id)
select new {Id = myRecords.id};
Filtering on two sets of results works the same way, in that you can filter on any common property shared by the two sets:
string[] cities = { "London", "Paris", "Seattle" };
var query = dataContext.Customers.Where (c => cities.Contains (c.City));

have you tried using something like this:
int[] inKeyword = { 5, 7, 9 };
var q = from o in db.Orders.Where(p => inKeyword.Contains(p.Identifier));
Hope it helps :)

Related

How to group a table by a certain attribute then count those elements that are present in an other table (with foreign keys) in LINQ?

Let's say I have two tables (entities):
class Person
{
public int Id { get; set; } // primary key
public string City { get; set; } // the attribute to group by
}
class JoinTable
{
public int Id { get; set; } // primary key
public int Person_Id { get; set; } // foreign key referencing a Person entity/row
public int SomeOther_Id { get; set; } // foreign key referencing some other irrelevant entity/row
}
I want to group all Person entities by their "City" attribute and count how many people are referenced in the JoinTable by each city.
How do I query that in LINQ?
I'm not quite sure, what you want to acchieve. But i think something like this:
// Example Data (would be great if you could write some in your questions..)
List<Person> ps = new List<Person>()
{
new Person() { Id = 1, City = "Cologne" },
new Person() { Id = 2, City = "Cologne" },
new Person() { Id = 3, City = "Berlin" },
new Person() { Id = 4, City = "Berlin" },
};
List<JoinTable> join = new List<JoinTable>()
{
new JoinTable() { Id = 1, Person_Id = 1, SomeOther_Id = 1000 },
new JoinTable() { Id = 1, Person_Id = 1, SomeOther_Id = 2000 },
new JoinTable() { Id = 1, Person_Id = 2, SomeOther_Id = 1000 },
new JoinTable() { Id = 1, Person_Id = 2, SomeOther_Id = 2000 },
new JoinTable() { Id = 1, Person_Id = 3, SomeOther_Id = 3000 },
new JoinTable() { Id = 1, Person_Id = 3, SomeOther_Id = 4000 },
};
// Join the Table and select a new object.
var tmp = ps.Join(
join, // which table/list should be joined
o => o.Id, // key of outer list
i => i.Person_Id, // key of inner list
(o, i) => new { City = o.City, Id = i.Id, SomeOtherId = i.SomeOther_Id}); // form a new object with three properties..
// now we can group out new objects
var groupingByCity = tmp.GroupBy(g => g.City);
// let's see what we got..
foreach (var g in groupingByCity)
{
Console.WriteLine(g.Key + ": " + g.Count());
foreach (var g2 in g.GroupBy(a => a.SomeOtherId)) // And yes we can group out values again..
{
Console.WriteLine(" " + g2.Key + ": " + g2.Count());
}
}

Cannot group data in LINQ

I have a question about a LINQ grouping.
I thought that grouping would be a simple matter of using the GroupBy function on the result set and specifying what to group it by. However my items appear to not be grouping together and instead are displaying as if the GroupBy function wasn't there. I want to group by the itemPk, but I'm can't seem to do it. I have tried grouping by both category.ItemFk and Item.Itempk, but no luck. Could someone give me a pointer on this?
var itemIds = items.Select(i => i.ItemId).ToList();
var itemAndCatJoin =
from item in Context.SCS_Items
join category in Context.SCS_ItemCategories
on item.ItemPk equals category.ItemFk
into temp
from category in temp.DefaultIfEmpty()
select new ExportItemTable
{
Category = category,
Item = item
};
return itemAndCatJoin.Where(i => itemIds.Contains(i.Item.ItemPk))
.GroupBy(n => new {n.Item, n.Category})
.Select(i => new ExportableItem
{
ItemPk = i.Key.Item.ItemPk,
Name = i.Key.Item.Name,
Description = i.Key.Item.Description,
Price = i.Key.Item.Price,
Category = i.Key.Category.Category.Category_Name,
GLDepartment = i.Key.Category.GL_Department.Name ?? "",
GLName = i.Key.Category.GL_Name.Name ?? "",
StartDate = i.Key.Item.StartDate,
EndDate = i.Key.Item.EndDate,
FiscalYear = i.Key.Item.SCS_FiscalYear.Name,
School = i.Key.Item.School != null ? i.Key.Item.School.School_Name : i.Key.Item.Board.Board_Name,
Beneficiary = i.Key.Item.SCS_Beneficiary.Name,
Quantity = i.Key.Item.MaxQuantity,
Deleted = i.Key.Item.DeletedFlag,
OptionalStudents = i.Key.Item.SCS_Attachments.Where(a => !a.IsRequired).SelectMany(a => a.SCS_StudentAttachments).Where(s => !s.DeletedFlag).Select(s => s.StudentFk).Distinct().Count(),
RequiredStudents = i.Key.Item.SCS_Attachments.Where(a => a.IsRequired).SelectMany(a => a.SCS_StudentAttachments).Where(s => !s.DeletedFlag).Select(s => s.StudentFk).Distinct().Count(),
IsPublic = i.Key.Item.IsPublic,
AllowRecurring = i.Key.Item.AllowRecurringPayments,
EffectiveCutoff = i.Key.Item.SCS_Attachments.Where(a => !a.DeletedFlag && a.CourseDropCutoff.HasValue).Select(a => a.CourseDropCutoff).OrderBy(a => a).FirstOrDefault(),
CreatedDate = i.Key.Item.CreatedDate
}).OrderBy(i => i.ItemPk).ToList();
}
your groupbyy is indeed doing nothing for you, you need to tell the groupby what to group by....
like
.GroupBy(n => n.Category)
Here is a simple example to your grouping question:
class Program
{
static void Main()
{
var allItems = GetAllItems();
var groups = from item in allItems
group item by item.Category
into newGroup
select newGroup;
foreach (var group in groups)
{
Console.WriteLine($"\nCategory: {group.Key}");
foreach (var item in group)
{
Console.WriteLine($"{item.Name}: {item.Price}");
}
}
Console.ReadLine();
}
static List<Category> GetAllCategories()
{
return new List<Category>()
{
new Category() { Id = 1, Name = "Programming Books" },
new Category() { Id = 2, Name = "Fiction Books" }
};
}
static List<Item> GetAllItems()
{
return new List<Item>()
{
new Item() { Id = 1, Name = "Embedded Linux", Category = 1, Price = 9.9 },
new Item() { Id = 2, Name = "LINQ In Action", Category = 1, Price = 36.19 },
new Item() { Id = 3, Name = "C# 6.0 and the .NET 4.6 Framework", Category = 1, Price = 40.99 },
new Item() { Id = 4, Name = "Thinking in LINQ", Category = 1, Price = 36.99 },
new Item() { Id = 5, Name = "The Book Thief", Category = 2, Price = 7.99 },
new Item() { Id = 6, Name = "All the Light We Cannot See", Category = 2, Price = 16.99 },
new Item() { Id = 7, Name = "The Life We Bury", Category = 2, Price = 8.96 }
};
}
}
public class Item
{
public int Id { get; set; }
public string Name { get; set; }
public double Price { get; set; }
public int Category { get; set; }
}
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
}
This example is simple enough for anyone new to LINQ. I am sure you can make some adjustment to make it work for your specific issue. Hope this will help.

How to set a value in one list when comparing it with other list using LINQ

I have two lists:
List A List B
ID FirstName WorkingID ID FirstName WorkingID
5 John Null 5 John 1
9 Patrick Null 9 Patrick 2
16 Ryan Null 16 Ryan 3
I want to compare these lists using LINQ and if IDs from both lists are equal, I want to set WorkingID in the first list, A list.
Thanks.
Also like this:
A.ForEach(c => c.WorkingID = B.Where(m => m.ID == c.ID).Select(s => s.WorkingID).FirstOrDefault());
Full source:
public class Test
{
public int ID;
public int? WorkingID;
}
class Program
{
static void Main(string[] args)
{
var A = new List<Test>()
{
new Test { ID = 5, WorkingID = null },
new Test { ID = 9, WorkingID = null },
new Test { ID = 16, WorkingID = null },
new Test { ID = 18, WorkingID = null }
};
var B = new List<Test>()
{
new Test { ID = 5, WorkingID = 1 },
new Test { ID = 9, WorkingID = 2 },
new Test { ID = 16, WorkingID = 3 }
};
A.ForEach(c => c.WorkingID = B.Where(m => m.ID == c.ID).Select(s => s.WorkingID).FirstOrDefault());
}
}
Try this:
listA.Where(a => listB.Any(b => a.ID == b.ID))
.ToList()
.ForEach(a => a.WorkingID = listB.First(b => b.ID == a.ID).WorkingID);
You could try something like this (not a pure LINQ approach):
foreach(var item in listA)
{
var itemInB = listB.FirstOrDefault(x=>x.ID==item.ID);
if(itemInB!=null)
item.WorkingID = itemInB.WorkongID
}
var query = from a in lista
join b in listb on a.ID equals b.ID
where b.WorkingID != null
select new {a, b};
foreach (var item in query)
{
item.a.WorkingID = item.b.WorkingID;
}
easy as that.
Here is one way, assuming a class structure as follows:
class MyItem
{
public int ID { get; set; }
public string FirstName { get; set; }
public int WorkingID { get; set; }
}
You can join the second list on matching IDs:
var list1 = new List<MyItem>() {
new MyItem() { ID = 5, FirstName = "John" },
new MyItem() { ID = 9, FirstName = "Patrick" },
new MyItem() { ID = 16, FirstName = "Ryan" },
};
var list2 = new List<MyItem>() {
new MyItem() { ID = 5, FirstName = "John", WorkingID = 1 },
new MyItem() { ID = 9, FirstName = "Patrick", WorkingID = 2 },
new MyItem() { ID = 16, FirstName = "Ryan", WorkingID = 3 },
};
var mergedList = from item1 in list1
join item2 in list2
on item1.ID equals item2.ID
select new MyItem() { ID = item1.ID, FirstName = item1.FirstName, WorkingID = item2.WorkingID };
I think using Join then looping through the list would be more efficient. The below is practically the same as the answer provided by Florian Schmidinger:
A.AsEnumerable().Join(B.AsEnumerable(), a => a.ID, b => b.ID, (a, b) => new { a, b })
.ToList().ForEach(obj => { obj.a.WorkingID = obj.b.WorkingID; });

How to make a LINQ query for this?

Okay, stuck for a day now, please somebody give a helping hand!
I want to select the quantity of EVERY possible item that a specific customer has, regardless of whether they have ordered the item or not. So if they haven't ordered it (i.e. it is not in the Orders table) then return a value of 0 for the quantity.
Orders CustomerId ItemId Quantity
Items
ItemId
ItemName
Customers
CustomerId
CustomerName
I'm guessing that this will involve some kind of subquery, but really not sure.
from c in customers
join o in Orders
on c.CustomerId == o.CustomerId
select c.CustomerName, o.Quantity
where c.CustomerId == customerId
This one caught my interest so I wrote a console app to do it.
Andomar answered a few moments before I did (+1) from me.
This is my independent solution but very close to his and no simpler...
class Program {
private class CustomerDto {
public int CustomerId { get; set; }
public string CustomerName { get; set; }
}
private class ItemDto {
public int ItemId { get; set; }
public string ItemName { get; set; }
}
private class OrderDto {
public int Id { get; set; }
public int ItemId { get; set; }
public int CustomerId { get; set; }
public int Quantity { get; set; }
}
private class CustomerOrderDto {
public int CustomerId { get; set; }
public int ItemId { get; set; }
public int TotalQuantity { get; set; }
}
static void Main(string[] args) {
List<CustomerDto> Customers = new List<CustomerDto>() {
new CustomerDto() { CustomerId = 1, CustomerName = "one"},
new CustomerDto() { CustomerId = 2, CustomerName = "two"},
new CustomerDto() { CustomerId = 3, CustomerName = "three"}
};
List<ItemDto> Items = new List<ItemDto>() {
new ItemDto() { ItemId = 1, ItemName = "item one"},
new ItemDto() { ItemId = 2, ItemName = "item two"},
new ItemDto() { ItemId = 3, ItemName = "item three"}
};
// customer1 has 2 orders for item 1, 0 for item 2 or 3
// customer2 has 1 order for item 2, 0 for item 1 or 3
// customer3 has 1 order for item 2, 1 order for item 3 and 0 for item 1
List<OrderDto> Orders = new List<OrderDto>() {
new OrderDto() { Id = 1, CustomerId = 1, ItemId = 1, Quantity = 3 },
new OrderDto() { Id = 1, CustomerId = 1, ItemId = 1, Quantity = 5 },
new OrderDto() { Id = 1, CustomerId = 3, ItemId = 2, Quantity = 5 },
new OrderDto() { Id = 1, CustomerId = 3, ItemId = 3, Quantity = 5 },
new OrderDto() { Id = 1, CustomerId = 2, ItemId = 2, Quantity = 5 }
};
List<CustomerOrderDto> results = (from c in Customers
from i in Items
join o in Orders on
new { c.CustomerId, i.ItemId } equals
new { o.CustomerId, o.ItemId } into oj
from o in oj.DefaultIfEmpty()
let x = o ?? new OrderDto() { CustomerId = c.CustomerId, ItemId = i.ItemId, Quantity = 0 }
group x by new { x.CustomerId, x.ItemId } into g
select new CustomerOrderDto() {
CustomerId = g.Key.CustomerId,
ItemId = g.Key.ItemId,
TotalQuantity = g.Select(x => x.Quantity).Sum()
}
).ToList();
foreach (var result in results) {
Console.WriteLine("Customer {0} purchased {1} units of item {2}",
result.CustomerId, result.TotalQuantity, result.ItemId);
}
Console.ReadKey(true);
}
}
In SQL, this is quite simple:
select c.name
, i.name
, isnull(sum(o.quantity),0)
from customers c
cross join
items i
left join
orders o
on o.customerid = c.customerid
and o.itemid = o.itemid
group by
c.name
, i.name
Translated to LINQ, this becomes:
from c in Customers
from i in Items
join o in Orders
on new { c.Customerid, i.Itemid }
equals new { o.Customerid, o.Itemid }
into o1
from o2 in o1.DefaultIfEmpty()
group o2.Quantity
by new { CustomerName = c.Name, ItemName = i.Name }
into q
select new {
q.Key.CustomerName,
q.Key.ItemName,
Quantity = q.Sum() ?? 0
}
I hope someone else posts a simpler LINQ version, because this looks insanely complex :)
The LINQ expresssion
from c in Customers
from i in Items
select ...
is a cross join of all customers and items. So the only thing left to do is to sum up the quantities for each:
var result = from c in Customers
from i in Items
select new
{
Customer = c,
Item = i,
Quantity = (from o in Orders
where o.CustomerId == c.CustomerId && o.ItemId == i.ItemId
select o.Quantity).Sum(),
};
Note that Sum returns 0 for an empty IEnumerable<int> (i.e. no orders found).
Linq
from t in Customers
join t2 in Orders on t.CustomerId equals (int)t2.CustomerId into t2Join
from t2 in t2Join.DefaultIfEmpty()
group t by t into t_g
select new
{
CustomerId = t_g.Key.CustomerId,
OrdersId = t_g.Key.OrdersId,
Quantity = t_g.Key.Orders.Where(c => c.CustomerId == t_g.Key.CustomerId)
.Sum(c => c.Quantity)
};
This is not tested but you can try.

Using Linq to select maximum value in a group

Oh, I just find that the error is caused by another part of code.
Case closed.
I have 2 tables
1- userinfo
id uid name
1 11 Billy
2 22 Paul
3 33 Joshua
2- Score
id uid score
1 11 30
2 22 40
3 11 50
4 11 60
5 33 20
6 33 70
7 33 80
I have a class called ScoreUser
public class ScoreUser{
public long uid{get; set;}
public string name{get;set;}
public int score{get;set;}
}
I want to use linq to query the above two tables, get the maximum score of each user and map it into the ScoreUser Object.
I use the following code:
from s in Scores
join i in UserInfos
on s.uid equals i.uid
group uscore by new { s.uid, i.name} into g
let maxScore = g.Max(p => p.score)
select new ScoreUser
{
uid = g.Key.uid,
name = g.Key.name,
score = maxScore
}
However, this code does does not work. It produces 7 objects instead of 3.
What should I do?
You are also grouping by score when it should be the aggregator. Try this:
from s in Scores
join i in UserInfos on s.uid equals i.uid
group by new { s.uid, i.name } into g
select new ScoreUser
{
uid = g.Key.uid
name = g.Key.name,
score = g.Max(p => p.score)
}
(update)
I see you found the problem. However I leave you here a test to this query:
class UserInfo
{
public int Id { get; set; }
public int UId { get; set; }
public string Name { get; set; }
}
class Score
{
public int Id { get; set; }
public int UId { get; set; }
public int SScore { get; set; }
}
public class ScoreUser
{
public int uid { get; set; }
public string name { get; set; }
public int score { get; set; }
public override string ToString()
{
return string.Format("UId:{0} Name:{1} Score:{2}", uid, name, score);
}
}
static void Main(string[] args)
{
List<UserInfo> infos = new List<UserInfo>()
{
new UserInfo {Id = 1, UId = 11, Name = "Billy"},
new UserInfo {Id = 2, UId = 22, Name = "Paul"},
new UserInfo {Id = 3, UId = 33, Name = "Joshua"}
};
List<Score> scores = new List<Score>()
{
new Score {Id = 1, UId = 11, SScore = 30},
new Score {Id = 2, UId = 22, SScore = 40},
new Score {Id = 3, UId = 11, SScore = 50},
new Score {Id = 4, UId = 11, SScore = 60},
new Score {Id = 5, UId = 33, SScore = 20},
new Score {Id = 6, UId = 33, SScore = 70},
new Score {Id = 7, UId = 33, SScore = 80}
};
var qry = from s in scores
join i in infos on s.UId equals i.UId
group s by new { s.UId, i.Name } into g
select new ScoreUser
{
uid = g.Key.UId,
name = g.Key.Name,
score = g.Max(p => p.SScore)
};
foreach (var su in qry)
{
Console.WriteLine(su);
}
}
Prints:
UId:11 Name:Billy Score:60
UId:22 Name:Paul Score:40
UId:33 Name:Joshua Score:80
for someone's flavor, providing here two LINQ using lambda expressions to select maximum value in a group
LINQ using lambda expressions
qry = Scores.Join(UserInfos, s => s.uid, i => i.uid, (s, i) => new { s, i })
.GroupBy(g => new { g.s.uid, g.i.name })
.Select(g => new ScoreUser
{
uid = g.Key.uid,
name = g.Key.name,
score = g.Max(p => p.s.score)
});
LINQ(Lambda) using LastOrDefault() to get Max() value, so the class ScoreUser() can be eliminated here.
var qry = Scores.Join(UserInfos, s => s.uid, i => i.uid, (s, i) => new { s, i })
.GroupBy(g => new { g.s.uid, g.i.name })
.Select(g => new { su = g.OrderBy(i => i.s.score) })
.Select(x => x.su.LastOrDefault()).ToList();
Both get same results as LINQ with query syntax.
This document may be interested
LINQ Query Syntax versus Method Syntax.
var list = records.GroupBy(p => p.Year, (key, g) => g.OrderByDescending(y => y.Month).First()).ToList();
Above query will return a list that includes the highest month item by grouping the years.

Categories