I have a very simple linq query that gets data from two datatables (orderHeader & OrderDetails) and joins them together. What I would like to do is take the order items for each order and pass them to a method for processing.
Here is the query:
var results = from orderHeader in dtOrderHeader.AsEnumerable()
join orderDetails in dtOrderDetails.AsEnumerable() on
orderHeader.Field<int>("order_ref") equals
orderDetails.Field<int>("order_id")
select new {
orderID = orderHeader.Field<int>("order_ref"),
orderItem = orderDetails.Field<string>("product_details")
};
What is the best way to iterate the results for each order?
Thanks
This result set contains multiple orders, this would require a nested foreach
foreach (var order in results.Select(r => r.orderID).Distinct()) {
Console.WriteLine("Order: " + order);
Console.WriteLine("Items:");
foreach (var product in results.Where(r => r.orderItem == order)) {
Console.WriteLine(product.orderItem);
}
}
Related
How do I select two or more values from a collection into a list using a single lambda expression? Here is what I am trying:
List<Prodcut> pds=GetProducts();
List<Product> pdl = new List<Product>();
foreach (Product item in pds)
{
pdl.Add(new Product
{
desc = item.Description,
prodId = Convert.ToInt16(item.pId)
});
}
GetProducts() returns a list of Products that have many (about 21) attributes. The above code does the job but I am trying to create a subset of the product list by extracting just two product attributes (description and productId) using a single lambda expression. How do I accomplish this?
What you want to do is called projection, you want to project each item and turn them into something else.
So you can use a Select:
var pdl = pds.Select(p => new Product
{
desc = p.Description,
prodId = Convert.ToInt16(p.pId)
}).ToList();
Have searched ant tested many examples in this forum but can't get a fully working method.
I am using linq to bulk insert a list of entity classes (RemoteReadings).
Due to unique constraints I need to filter out any items already inserted.
Uniqiuness is composed of 2 columns meterid and datetime in RemoteReadings table.
// approx 5000 records (I need to do this in batches of 2000 due to a
// constraint in L2S,but can do this after get this working)
List<RemoteReading> lst = createListFromCSV();
// Option 1:
// This does not work as am comparing memory list to db list. I need to use contains() method.
// Actually am trying to accomplish this infollowing examples.
List<RemoteReading> myLst = (from ri in db.RemoteReadings
from l in lst
where l.meterid = ri.meterid
&& l.date = r.date
select ri).ToList();
////
// Option2:
// Get the list from DB that are in memory lst
List<RemoteReading> myLst = (from ri in db.RemoteReadings
where
// where in this list by comparing meaterid and datemeaured
(from l in lst
select
/// help here !
///
select ri).ToList<RemoteInterconnectorReading>();
// Option3:
// Get the list from lst that are not in database
// I am bit confused here !
// Tried also to remove from list any duplicates:
List<RemoteReading> result = List<RemoteReading>)myLst.Except(lst).ToList<RemoteReading>();
// Ultimately
db.RemoteReading.InsertAllOnSubmit(result);
db.submitChanges();
Any help please?
Due to limitations in EF, we can't join DB query with in-memory list. Also, Contains can only be used with primitive list. So we need to make some efforts to find the duplicates on two columns.
var newItems = createListFromCSV();
var meterIds = newItems.Select(n=> n.meterid).Distinct().ToList();
var dates = newItems.Select(n=> n.date).Distinct().ToList();
var probableMatches = (from ri in db.RemoteReadings
where (meterIds.Contains(ri.meterids)
|| dates.Contains(ri.date)
select new {ri.merterid, ri.date}).ToList();
var duplicates = (from existingRi in probaleMatches
join newRi in newItems
on new {existingRi.meterid, existingRi.date}
equals {newRi.meterid, newRi.date}
select newRi).ToList();
var insertList = newItems.Except(duplicates).ToList();
db.RemoteReadings.Insert(insertList); // or whatever
With the great help of aSharma and some other tweaks, I finally got a working and tested method. As my lists contain over 5000 items I had to execute in batches to override the 2112 SQL RPC call limitation. Added some comments and credits :)
/// List<RemoteReadings> contains a list of database Entity Classes RemoteReadings
public List<RemoteReadings> removeDublicatesFirst(List<RemoteReadings> lst)
{
try
{
DataClasses1DataContext db = new DataClasses1DataContext();
var meterIds = lst.Select(n => n.meterId).Distinct().ToList();
var dates = lst.Select(n => n.mydate).Distinct().ToList();
var myfLst = new List<RemoteReadings>();
// To avoid the following SqlException, Linq query should be exceuted in batches as follows.
//{System.Data.SqlClient.SqlException
// The incoming tabular data stream (TDS) remote procedure call (RPC) protocol stream is incorrect.
// Too many parameters were provided in this RPC request. The maximum is 2100.
foreach (var batch in dates.Batch(2000))
{
// Gets a list of possible matches from DB.
var probableMatches = (from ri in db.RemoteReadingss
where (meterIds.Contains(ri.meterId)
&& batch.Contains(ri.mydate))
select new { ri.meterId, ri.mydate }).ToList();
// Join the probableMatches with the lst in memory on unique
// constraints meterid.date to find any duplicates
var duplicates = (from existingRi in probableMatches
join newRi in lst
on new
{
existingRi.meterId,
existingRi.mydate
}
equals new { newRi.meterId, newRi.mydate }
select newRi).ToList();
//Add duplicates in a new List due to batch executions.
foreach (var s in duplicates)
{
myfLst.Add(s);
}
}
// Remove the duplicates from lst found in myfLst;
var insertList = lst.Except(myfLst).ToList();
return insertList;
}
catch
(Exception ex)
{
return null;
}
}
// Found this extension Class to divide IEnumerable in batches.
// http://stackoverflow.com/a/13731854/288865
public static class MyExtensions
{
public static IEnumerable<IEnumerable<T>> Batch<T>(this IEnumerable<T> items,
int maxItems)
{
return items.Select((item, inx) => new { item, inx })
.GroupBy(x => x.inx / maxItems)
.Select(g => g.Select(x => x.item));
}
}
In the DB, I have a two tables with a one-to-many relationship:
orders suborders
----------- -----------
id id
name order_id
name
I'd like to query these tables and end up with a list of order objects, each of which contains a list (or empty list) of suborder objects. I'd also like to do this in a single DB query so it performs well.
In traditional SQL query land, I'd do something like (forgive the pseudocode):
rs = "select o.id, o.name, so.id, so.name from orders o left join suborders so on o.id = so.order_id order by o.id"
orders = new List<Order>
order = null
foreach (row in rs) {
if (order == null || row.get(o.id) != order.id) {
order = new Order(row.get(o.id), row.get(o.name), new List<Suborders>)
orders.add(order)
}
if (row.get(so.id) != null) {
order.suborders.add(new Suborder(row.get(so.id) row.get(so.name))
}
}
Is there a way to get this same resulting object structure using LINQ-to-Entities? Note that I want to get new objects out of the query, not the Entity Framework generated objects.
The following gets me close, but throws an exception: "LINQ to Entities does not recognize the method..."
var orders =
(from o in Context.orders
join so in Context.suborders on o.id equals so.order_id into gj
select new Order
{
id = o.id,
name = o.name,
suborders = (from so in gj select new Suborder
{
so.id,
so.name
}).ToList()
}).ToList();
The solution ends up being pretty simple. The key is to use a group join to get SQL to do the left join to suborders, and add a second ToList() call to force the query to be run so you're not trying to do object creation on the SQL server.
orders = Context.orders
.GroupJoin(
Context.suborders,
o => o.id,
so => so.order_id,
(o, so) => new { order = o, suborders = so })
.ToList()
.Select(r => new Order
{
id = r.order.id,
name = r.order.name,
suborders = r.suborders.Select(so => new Suborder
{
id = so.id,
name = so.name
}.ToList()
}).ToList();
This code only makes a single query to SQL for all objects and their child objects. It also lets you transform the EF objects into whatever you need.
I Always create a virtualized Property for Relations
so just extend (add a property) to your order class :
public class Order{
...
List<Suborder> _suborders;
public List<Suborder> Suborders{
get {
return _suborders ?? (_suborders = MyContext.Suborders.Where(X=>X.order_id==this.id).ToList());
}
...
}
so data will be fetched (pulled) only when you call the getters
How about this code ?
You can get a local cache.
List<Orders> orders = new List<Orders>();
private void UpdateCache(List<int> idList)
{
using (var db = new Test(Settings.Default.testConnectionString))
{
DataLoadOptions opt = new DataLoadOptions();
opt.LoadWith<Orders>(x => x.Suborders);
db.LoadOptions = opt;
orders = db.Orders.Where(x => idList.Contains(x.Id)).ToList();
}
}
private void DumpOrders()
{
foreach (var order in orders)
{
Console.WriteLine("*** order");
Console.WriteLine("id:{0},name:{1}", order.Id, order.Name);
if (order.Suborders.Any())
{
Console.WriteLine("****** sub order");
foreach (var suborder in order.Suborders)
{
Console.WriteLine("\torder id:{0},id{1},name:{2}", suborder.Order_id, suborder.Id, suborder.Name);
}
}
}
}
private void button1_Click(object sender, EventArgs e)
{
UpdateCache(new List<int> { 0, 1, 2 });
DumpOrders();
}
Output example below
*** order
id:0,name:A
****** sub order
order id:0,id0,name:Item001
order id:0,id1,name:Item002
order id:0,id2,name:Item003
*** order
id:1,name:B
****** sub order
order id:1,id0,name:Item003
*** order
id:2,name:C
****** sub order
order id:2,id0,name:Item004
order id:2,id1,name:Item005
I have a database that looks like this:
tbl_Seminar
ID
isActive
tbl_SeminarFees
ID
seminar_id -- foreign key
fee_text
I want to get all seminars that are active (isActive ==1) and a list of the fees associated with that seminar. Each Seminar may have n records in tbl_SeminarFees that are its fees. I am able to return a linq structure that returns me a list of objects that look like this {seminar, SeminarFee} but I wanted to create a nested structure that looks like this:
{seminar, List<SeminarFee>}
What should my linq query look like?
here is my linq currently:
var results = from s in context.Seminar
join p in context.SeminarFees on
s.ID equals p.SeminarID
where s.IsActive == 1
select new
{
Seminar = s,
Fees = p
};
How do I change this to get a list of these: {seminar, List<SeminarFee>}
Thanks
UPDATE
#lazyberezovsky gave me a good idea to use a group join and into another variable. But then how do I loop through the result set. Here is what I have now:
foreach (var seminarAndItsFeesObject in results)
{
//do something with the seminar object
//do something with the list of fees
}
This however gives me the following error:
Argument type 'SeminarFees' does not match the
corresponding member type
'System.Collections.Generic.IEnumerable`1[SeminarFees]'
What am I doing wrong?
Thanks
You can use group join which groups inner sequence items based on keys equality (a.k.a. join..into) to get all fees related to seminar:
var results = from s in context.Seminar
join f in context.SeminarFees on
s.ID equals f.SeminarID into fees // here
where s.IsActive == 1
select new
{
Seminar = s,
Fees = fees
};
You can't call ToList() on server side. But you can map results on client later.
BTW You can define navigation property Fees on Seminar object:
public virtual ICollection<SeminarFee> Fees { get; set; }
In this case you will be able load seminars with fees:
var results = context.Seminar.Include(s => s.Fees) // eager loading
.Where(s => s.IsActive == 1);
var results = from s in context.Seminar
join p in context.SeminarFees on s.ID equals p.SeminarID
where s.IsActive == 1
group p by s into grouped
select new {
Seminar = grouped.Key,
Fees = grouped.ToList()
};
Maybe I'm going about this the wrong way...
I have an Order table and an OrderItem table. I create a new Order using linq2sql generated classes.
I then attempt to get all orderable items out of my database using a query that goes after various tables.
I try to then create a new list of OrderItem from that query, but it squawks that I can't explicitly create the object.
Explicit construction of entity type OrderItem in query is not allowed.
Here is the query:
return (from im in dc.MasterItems
join c in dc.Categories
on im.CATEGORY equals c.CATEGORY1
select new OrderItem()
{
OrderItemId = im.ItemId
});
The idea is to populate the database with all orderable items when a new order is created, and then display them in a grid for updates. I'm taking the results of that query and attempting to use AddRange on Order.OrderItems
Is there a proper strategy for accomplishing this using linq2sql?
Thanks in advance for your help.
From my understanding of L2S, I don't think you can use explicit construction (in other words new SomeObj() { ... }) in a query because you aren't enumerating the results yet. In other words, the query has just been built, so how are you supposed to do this:
SELECT new OrderItem() FROM MasterItems im JOIN Categories c on c.CATEGORY1 = im.CATEGORY
This is what you're trying to do, which doesn't work because you can't return a POCO (unless you join the OrderItem back somehow and do OrderItem.* somewhere). Ultimately, what you would have to do is just enumerate the collection (either in a foreach loop or by calling ToList()) on the query first and then build your OrderItem objects.
var query = (from im in dc.MasterItems
join c in dc.Categories
on im.CATEGORY equals c.CATEGORY1
select new { MasterItem = im, Category = c});
List<OrderItem> returnItems = new List<OrderItem>();
foreach(var item in query)
{
returnItems.Add(new OrderItem() { OrderItemId = item.MasterItem.ItemId });
}
return returnItems;
OR
return (from im in dc.MasterItems
join c in dc.Categories
on im.CATEGORY equals c.CATEGORY1
select new { MasterItem = im, Category = c})
.ToList()
.Select(tr => new OrderItem() { OrderItemId = tr.MasterItem.ItemId });
Try that out and let me know if that helps.
Expand the order class by creating a partial file where that class OrderItem now has property(ies) which lend itself to business logic needs, but don't need to be saved off to the database.
public partial class OrderItem
{
public int JoinedOrderItemId { get; set; }
public bool HasBeenProcessed { get; set; }
}