Acces data from objects in a list - c#

Hi I am trying to convert a a LINQ query result into a list of objects and I seem to be doing something wrong because I can not acces the properties of each object.Here is my code:
List<Object> productList = new List<Object>();
var products = (from p in Products
join s in SubCategories on p.SubcatId equals s.SubCatId
join b in Brands on p.BrandId equals b.BrandId
select new
{
Subcategory = s.SubCatName,
Brand = b.BrandName,
p.ProductName,
p.ProductPrice
}).Where(x => x.Subcategory == subcategory);
foreach (var product in products)
{
productList.Add(product);
}
foreach (var produs in productList){
Console.WriteLine(produs.ProductName);
}
When I try to do this I get an error that says:
Object does not contain a definion for ProductName
The same goes for all the other fields two
Aldo if I try and do this:
Console.WriteLine(produs);
I get tables with the data for each field
I have run this for tests on LINQPAD and it also does not work in visual studio.What am I doing wrong?

This is the problem:
List<Object> productList = new List<Object>();
You're declaring it as a List<object>, which means when you later write this:
foreach (var produs in productList)
Then produs is implicitly typed as object.
The simplest approach would be just to use ToList() instead of copying the results to a list yourself:
var products = (from p in Products
join s in SubCategories.Where(x => x.SubCatName == subcategory)
on p.SubcatId equals s.SubCatId
join b in Brands on p.BrandId equals b.BrandId
select new {
Subcategory = s.SubCatName,
Brand = b.BrandName,
p.ProductName,
p.ProductPrice
}).ToList();
Note that I've moved the subcategory name filter as early as possible - there's no need to do it after the join. The ToList() call at the end will mean the result is a List<T> where T is your anonymous type.
Then you can use:
foreach (var product in products)
{
Console.WriteLine(produs.ProductName);
}

use
List<Product> productList = new List<Product>();
instead of
List<Object> productList = new List<Object>();
where Product is the unit of Products

A List of Object means that you only get the methods contained within an object. You should do something like:
List<Product> productList = new List<Product>();
Just as a note you can also do:
productList = products.ToList<Product>();
instead of iterating over the list and adding each element. (Linq does this for you implicitely).
NOTE: This is assuming that you change your query select to select new Product { ... }
This will then allow you to use List<Product> productList so that you can pass your Product list to other methods etc.

Related

Select Multiple Values from a Collection using Lambda Expression

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();

Get another class from LINQ to SQL query

I am a little weak in LINQ to SQL so will try to explain my problem.
I have a method as follows (simplified to explain it better):
public static List<ic_ProductData> GetCompleteSimilarProductsWithApplyButton(InfoChoiceAdminDataContext db)
{
var products = (from
p in db.ic_ProductDatas
join proddef in db.ic_ProductDefs on p.ProductDefId equals proddef.ProductDefId
select p
).ToList();
return products;
}
ic_ProductData and ic_ProductDefs are tables in my database
The ic_ProductData class contains a manually created property as:
public ic_ProductDef RelatedProductDef { get; set; }
I want to modify the above LINQ to SQL query so that I can populate this property.
Please note I do not want another call to the database.
Also there are a lot of properties in ic_ProductData so I want to avoid mapping each and every property
Something to the effect of the following (obviously the below is wrong):
public static List<ic_ProductData> GetCompleteSimilarProductsWithApplyButton(InfoChoiceAdminDataContext db)
{
var products = (from
p in db.ic_ProductDatas
join proddef in db.ic_ProductDefs on p.ProductDefId equals proddef.ProductDefId
//trying to change here
select new ic_ProductData
{
//do something with p here so that all the properties of new object gets filled
// avoid mapping of properties here
RelatedProductDef = proddef
}
).ToList();
return products;
}
With my limited knowledge I am stuck here.
Please help!
Thanks in advance!
You can do something like this:
var query = (from p in db.ic_ProductDatas
join proddef in db.ic_ProductDefs on p.ProductDefId equals proddef.ProductDefId
select new
{
ProductData = p,
Def = proddef
}).ToList();
List<ic_ProductData> products = new List<ic_ProductData>();
foreach( var product in query)
{
product.ProductData.RelatedProductDef = product.Def;
products.Add(product);
}
Basicly, you first need to do the one query to the database, this returns an anonymous type containing both your product and its Def.
Finally, you loop (in memory, no db-calls!) over these, creating your final objects with their RelatedProductDef properties populated.

Manipulate two list of objects in c#

I have an objectA with an ID property and a Status property.
I have a ListA which is a collection of objectA.
I also have an objectB with an ID property and a Status property.
I have a ListB which is a collection of objectB.
I need to match listA collection with listB collection based on ID and if there is a match update the Status from ListB to ListA.
What is the best way to do this without foreach loop?
Thanks for the help.
You can use LINQ for this:
var objectsForUpdate = (from a in listA
join b in listB
on a.Id equals b.Id
select new { a, b });
foreach (var obj in objectsForUpdate)
{
obj.a.Status = obj.b.Status;
}
Note this cannot be achieved without using the foreach statement.
I had a similar scenario. I had cartProducts and productsPurchasePrices.
I used linq to join the two collections:
//join currentCart...
var p = currentCart.Rows.Join(
productsPurchasePrices, //... with productsPurchasePrices
row => row.ProductCode, //first collection key
purchasePrice => purchasePrice.Key, //second collection key
(row, purchasePrice) => new
{
productCode = row.ProductCode,
productMargin = purchasePrice.ProductMargin
});
References:
http://msdn.microsoft.com/en-us/library/bb397941.aspx
http://code.msdn.microsoft.com/LINQ-Join-Operators-dabef4e9

How to add to linq2sql entity set for master detail if I can't explicitly define the detail?

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; }
}

LINQ Join 2 List<T>s

Preface: I don't understand what this does:
o => o.ID, i => i.ID, (o, id) => o
So go easy on me. :-)
I have 2 lists that I need to join together:
// list1 contains ALL contacts for a customer.
// Each item has a unique ID.
// There are no duplicates.
ContactCollection list1 = myCustomer.GetContacts();
// list2 contains the customer contacts (in list1) relevant to a REPORT
// the items in this list may have properties that differ from those in list1.
/*****/// e.g.:
/*****/ bool SelectedForNotification;
/*****/// may be different.
ContactCollection list2 = myReport.GetContacts();
I need to create a third ContactCollection that contains all of the contacts in list1 but with the properties of the items in list2, if the item is in the list[2] (list3.Count == list1.Count).
I need to replace all items in list1 with the items in list2 where items in list1 have the IDs of the items in list2. The resulting list (list3) should contain the same number of items at list1.
I feel as though I'm not making any sense. So, please ask questions in the comments and I'll try to clarify.
Joins are not so difficult, but your problem could probably use some further explanation.
To join two lists, you could do something like
var joined = from Item1 in list1
join Item2 in list2
on Item1.Id equals Item2.Id // join on some property
select new { Item1, Item2 };
this will give an IEnumerable<'a>, where 'a is an anonymous type holding an item from list1 and its related item from list2. You could then choose which objects' properties to use as needed.
To get the result to a concrete list, all that is needed is a call to .ToList(). You can do that like
var list3 = joined.ToList();
// or
var list3 = (from Item1 in list1
join Item2 in list2
on Item1.Id equals Item2.Id // join on some property
select new { Item1, Item2 }).ToList();
To do a left join to select all elements from list1 even without a match in list2, you can do something like this
var list3 = (from Item1 in list1
join Item2 in list2
on Item1.Id equals Item2.Id // join on some property
into grouping
from Item2 in grouping.DefaultIfEmpty()
select new { Item1, Item2 }).ToList();
This will give you a list where Item1 equals the item from the first list and Item2 will either equal the matching item from the second list or the default, which will be null for a reference type.
Here is what I came up with (based on this):
List<Contact> list3 = (from item1 in list1
join item2 in list2
on item1.ContactID equals item2.ContactID into g
from o in g.DefaultIfEmpty()
select o == null ? item1 :o).ToList<Contact>();
My favorite part is the big nosed smiley
:o)
Thanks for your help!
Here is a DotNetFiddle with a Linq Group Join
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
class Order
{
public int Id;
public string Name;
public Order(int id, string name)
{
this.Id = id;
this.Name = name;
}
}
class OrderItem
{
public int Id;
public string Name;
public int OrderId;
public OrderItem(int id, string name, int orderId)
{
this.Id = id;
this.Name = name;
this.OrderId = orderId;
}
}
List<Order> orders = new List<Order>()
{
new Order(1, "one"),
new Order(2, "two")
};
List<OrderItem> orderItems = new List<OrderItem>()
{
new OrderItem(1, "itemOne", 1),
new OrderItem(2, "itemTwo", 1),
new OrderItem(3, "itemThree", 1),
new OrderItem(4, "itemFour", 2),
new OrderItem(5, "itemFive", 2)
};
var joined =
from o in orders
join oi in orderItems
on o.Id equals oi.OrderId into gj // gj means group join and is a collection OrderItem
select new { o, gj };
// this is just to write the results to the console
string columns = "{0,-20} {1, -20}";
Console.WriteLine(string.Format(columns, "Order", "Item Count"));
foreach(var j in joined)
{
Console.WriteLine(columns, j.o.Name, j.gj.Count() );
}
It looks like you don't really need a full-join. You could instead do a semi-join, checking each contact in list 2 to see if it is contained in list 1:
ContactCollection list3 = list2.Where(c => list1.Contains(c));
I don't know how big your lists are, but note that this approach has O(nm) complexity unless list1 is sorted or supports fast lookups (as in a hashset), in which case it could be as efficient as O(nlog(m)) or rewritten as a merge-join and be O(n).

Categories