Get selected fields on a list using a LINQ query - c#

I have an entity framework generated class like this.
public partial class TBLM_PRODUCT
{
public string PRODUCT_CODE { get; set; }
public string PRODUCT_DESC { get; set; }
public string PRODUCT_ISBN { get; set; }
public string PRODUCT_SUPPLIER { get; set; }
public string PRODUCT_PROGROUP { get; set; }
}
Normally I select items list like this using a LINQ query.
using ( AEntities RAEntity = new AEntities())
{
RAEntity.TBLM_PRODUCT.ToList<DataControllers.TBLM_PRODUCT>();
}
I want to select an item list with two fields like this like as in following query
select PRODUCT_CODE,PRODUCT_DESC from TBLM_PRODUCT where PRODUCT_PROGROUP='GG';
How can I achieve that?

using ( AEntities RAEntity = new AEntities())
{
var all = RAEntity.TBLM_PRODUCT.ToList<DataControllers.TBLM_PRODUCT>();
var yourList = all
.Where(x => x.PRODUCT_PROGROUP == "GG")
.Select(p => new { p.PRODUCT_CODE, p.PRODUCT_DESC })
.ToList();
}

Don't select all records first and then filtered your data.
If you use .ToList<DataControllers.TBLM_PRODUCT>() then it can select all records. So instead of this you can select your columns at the time of query fired to database.
If your TBLM_PRODUCT is of any collection type like IEnumerable<> or IQueryable<> then,
using ( AEntities RAEntity = new AEntities())
{
var result = RAEntity.TBLM_PRODUCT.Where(x => x.PRODUCT_PROGROUP == "GG").Select(x => new { x.PRODUCT_CODE, x.PRODUCT_DESC }).ToList();
}

using (AEntities RAEntity = new AEntities())
{
var list= RAEntity.TBLM_PRODUCT
.Where(p => p.PRODUCT_PROGROUP == "GG")
.Select(p => new TBLM_PRODUCT { PRODUCT_CODE = p.PRODUCT_CODE, PRODUCT_DESC = p.PRODUCT_DESC })
.ToList();
}

Related

LINQ select all items of all subcollections that contain a string

I'm using jqueryui autocomplete to assist user in an item selection. I'm having trouble selecting the correct items from the objects' subcollections.
Object structure (simplified) is
public class TargetType
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<SubCategory> SubCategories { get; set; }
public TargetType()
{
SubCategories = new HashSet<SubCategory>();
}
}
public class SubCategory
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<SubTargetType> SubTargetTypes { get; set; }
public SubCategory()
{
SubTargetTypes = new HashSet<SubTargetType>();
}
}
Currently I'm doing this with nested foreach loops, but is there a better way?
Current code:
List<SubTargetResponse> result = new List<SubTargetResponse>();
foreach (SubCategory sc in myTargetType.SubCategories)
{
foreach (SubTargetType stt in sc.SubTargetTypes)
{
if (stt.Name.ToLower().Contains(type.ToLower()))
{
result.Add(new SubTargetResponse {
Id = stt.Id,
CategoryId = sc.Id,
Name = stt.Name });
}
}
}
You can do using Linq like this
var result = myTargetType.SubCategories
.SelectMany(sc => sc.SubTargetTypes)
.Where(stt => stt.Name.ToLower().Contains(type.ToLower()))
.Select(stt => new SubTargetResponse {
Id = stt.Id,
CategoryId = sc.Id,
Name = stt.Name });
The above query doesn't work. The following should work, but I'd not recommend that as that'd not be faster or more readable.
var result = myTargetType.SubCategories
.Select(sc => new Tuple<int, IEnumerable<SubTargetType>>
(sc.Id,
sc.SubTargetTypes.Where(stt => stt.Name.ToLower().Contains(type.ToLower()))))
.SelectMany(tpl => tpl.Item2.Select(stt => new SubTargetResponse {
Id = stt.Id,
CategoryId = tpl.Item1,
Name = stt.Name }));
Actually there are 2 different questions.
LINQ select all items of all subcollections that contain a string
Solutions:
(A) LINQ syntax:
var result =
(from sc in myTargetType.SubCategories
from stt in sc.SubTargetTypes.Where(t => t.Name.ToLower().Contains(type.ToLower()))
select new SubTargetResponse
{
Id = stt.Id,
CategoryId = sc.Id,
Name = stt.Name
})
.ToList();
(B) Method syntax:
var result =
myTargetType.SubCategories.SelectMany(
sc => sc.SubTargetTypes.Where(stt => stt.Name.ToLower().Contains(type.ToLower())),
(sc, stt) => new SubTargetResponse
{
Id = stt.Id,
CategoryId = sc.Id,
Name = stt.Name
})
.ToList();
Currently I'm doing this with nested foreach loops, but is there a better way?
Well, it depends of what do you mean by "better". Compare your code with LINQ solutions and answer the question. I personally do not see LINQ being better in this case (except no curly braces and different indentation, but a lot of a hidden garbage), and what to say about the second LINQ version in this answer - if that's "better" than your code, I don't know where are we going.

LINQ to Entities, Where Any In

How to write 'Where Any In' in LINQ to Entity?
Here is my model :
class Chair
{
public int Id { get; set; }
public int TableId { get; set; }
public Table Table { get; set; }
}
class Table
{
public int Id { get; set; }
public ICollection<Chair> Chairs { get; set; }
public ICollection<Category> Categories { get; set; }
public Table()
{
Chairs = new List<Chair>();
Categories = new List<Category>();
}
}
class Category
{
public int Id { get; set; }
public ICollection<Table> Tables { get; set; }
}
I also got a simple list of Category :
List<Category> myCategories = new List<Category>(c,d,e);
I want to get only that Chairs that belongs to Table that got one of the Category from myCategories List. Thats what im trying to do :
var result =
ctx.Chairs.Where(x => x.Table.Categories.Any(y => myCategories.Any(z => z.Id == y.Id))).ToList();
I think its ok but what i get is error :
"Unable to create a constant value of type 'ConsoleApplication1.Category'. Only primitive types or enumeration types are supported in this context"
Try to compare with in-memory categories Ids collection, instead of categories collection.
var myCategoriesIds = myCategories.Select(c => c.Id).ToArray();
var result =
context.Chairs
.Where(
x => x.Table.Categories.Any(
y => myCategoriesIds.Contains(y.Id)))
.ToList();
this is because ctx.Chairs is a collection that is in database, you should retrieve that collection first in order to compare it with in-memory data:
var result = ctx
.Chairs
.AsEnumerable() // retrieve data
.Where(x =>
x.Table.Categories.Any(y =>
myCategories.Any(z => z.Id == y.Id)))
.ToList();
EDIT: that wouldn't be the correct thing to do if you have a lot of entities on database, what you can do is to split it into two queries:
var tables = ctx.Tables
.Where(x =>
x.Categories.Any(y =>
myCategories.Any(z => z.Id == y.Id)));
var result = ctx.Chairs
.Where(x =>
tables.Any(t=> t.Id == x.TableId))
.ToList();
You can select Ids from myCategories and use it last statement.
var CategoryIds = myCategories.Select(ct => ct.Id);
var result = ctx.Chairs.Where(x => x.Table.Categories.Any(y => CategoryIds.Any(z => z == y.Id))).ToList();

Getting List of Model1 with List of Model2 in it LINQ - MVC 4 EntityFramework 5

I have a requirement where I need to get a List of Model1 (List) using Linq, the Model1 have List of Model2 (List) in it and I need to fetch that also. For this I have created a Linq but m getting following error:
LINQ to Entities does not recognize the method
'System.Collections.Generic.List1 [OurCourse]
ToList[OurCourse](System.Collections.Generic.IEnumerable1
[OurCourse])' method, and this method cannot be translated into a
store expression.
Please refer below for detail:
I have two tables Colleges and Courses, with following columns:
College: ID, Name, Contact, City, Address
Cource: ID, CollegeID, Name, Years
My project have two view models for them, as follows:
public class OurCollege
{
public string Name { get; set; }
public string Contact { get; set; }
public List<OurCourse> MyCourses { get; set; }
}
public class OurCourse
{
public string Name { get; set; }
public int NumberOfYears { get; set; }
}
Here the the query query which I have prepared but I am getting the error:
var colleges = db.Colleges
.Select(cld => new OurCollege()
{
Name = cld.Name,
Contact = cld.Contact,
MyCourses = cld.Course
.Select(crs => new OurCourse()
{
Name = crs.Name,
NumberOfYears = crs.Years
}).ToList()
}).ToList();
How about doing like this:
MyCourses = from crs in cld.Course
select new OurCourse
{
Name = crs.Name,
NumberOfYears = crs.Years
}
Your complete query will look now:
var colleges = db.Colleges
.Select(cld => new OurCollege()
{
Name = cld.Name,
Contact = cld.Contact,
MyCourses = (from crs in cld.Course
select new OurCourse
{
Name = crs.Name,
NumberOfYears = crs.Years
}).ToList()
}).ToList();
Actually LINQ to Entities converts your query into SQL.It doesn't know how to translate ToList() in SQL
An alternate is to change you List<T> to IEnumerable<T> and remove ToList() frim your original code:
public IEnumerable<OurCourse> MyCourses { get; set; }
and in query:
var colleges = db.Colleges
.Select(cld => new OurCollege()
{
Name = cld.Name,
Contact = cld.Contact,
MyCourses = cld.Course
.Select(crs => new OurCourse()
{
Name = crs.Name,
NumberOfYears = crs.Years
})
}).ToList();
For more details visit this Entity Framework ToList() in nested type (LINQ to Entities does not recognize the method)
A foreach loop will work, you can write the query something like this
var colleges = db.Colleges
.Select(x => new OurCollege()
{
CollegeId = x.CollegeId,
Name = x.Name,
Contact = x.Contact
}).ToList();
foreach (var college in colleges)
{
college.MyCourse = db.Course.Where(x => x.CollegeId == college.CollegeId)
.Select(x => new OurCourse()
{
Name = x.Name,
NumberOfYears = x.Years
}).ToList()
}

Linq "join" with a IList<T> getting "Error Unable to create a constant value.."

I have a Save Method that saves with a Linq query a manually re-orderd list (in a web form) that is passed as the parameter to my method, and I try to update the Order Property of the IEnumerable<VM_CategoryLabel> I retrieve from the database (EF) with the corresponding value in the list (maybe would that be clearer with my code below):
public static void SaveFromList(IList<VM_CategoryLabelExtra> listTemplate)
{
int idCat = listTemplate.Select(x => x.IdCat).FirstOrDefault();
var test = (int)listTemplate.Where(z => z.Id == 8).Select(z => z.Order).FirstOrDefault();
using (var context = new my_Entities())
{
var requete = from x in context.arc_CatLabel
where x.ID_Categorie == idCat
orderby x.Sequence_Cat
select new VM_CategoryLabel
{
Id = x.ID_LabelPerso,
//Order = x.Sequence_Cat,
Order = (int)listTemplate.Where(z => z.Id == x.ID_LabelPerso).Select(z => z.Order).First(),
Label = x.arc_Label.Label,
Unit = x.arc_Label.Unit
};
context.SaveChanges();
}
}
I used the "test" var to see if my "sub-query" gets the correct value, and it does, but when I use my Linq expression inside the Select (the commented Order line), I get the following error:
Unable to create a constant value of type 'Namespace.Models.VM_CategoryLabelExtra. "Only primitive types and enumeration types are supported in this context.
Here are my classes:
public class VM_CategoryLabel
{
public int Id { get; set; }
public int Order { get; set; }
public string Label { get; set; }
public string Unit { get; set; }
public bool Checked { get; set; }
}
public class VM_CategoryLabelExtra
{
public int Id { get; set; }
public int IdCat { get; set; }
public int Order { get; set; }
public string Label { get; set; }
public string Unit { get; set; }
public bool Checked { get; set; }
}
So I suppose that I should not query the list inside my query ? So how do I "match" the 2 lists of values ?
I also tried the following (after having replace in the Linq query: Order = x.Sequence_Cat)that is not working neither because the iteration variable is
read-only:
foreach (var item in requete)
{
item.Order = listTemplate.Where(x => x.Id == item.Id).Select(x => x.Order).FirstOrDefault();
}
try
{
context.SaveChanges();
I suggest using this.
It is the let clause.
public static void SaveFromList(IList<VM_CategoryLabelExtra> listTemplate)
{
int idCat = listTemplate.Select(x => x.IdCat).FirstOrDefault();
var test = (int)listTemplate.Where(z => z.Id == 8).Select(z => z.Order).FirstOrDefault();
using (var context = new my_Entities())
{
var requete = from x in context.arc_CatLabel
where x.ID_Categorie == idCat
orderby x.Sequence_Cat
let list = listTemplate
select new VM_CategoryLabel
{
Id = x.ID_LabelPerso,
Order = list.Where(z => z.Id == x.ID_LabelPerso).Select(z => z.Order).First(),
Label = x.arc_Label.Label,
Unit = x.arc_Label.Unit
};
context.SaveChanges();
}
}
edit: instead offrom you can just do let list = listTemplate
Should work now :)
example for let:
// The let keyword in query expressions comes in useful with subqueries: it lets
// you re-use the subquery in the projection:
from c in Customers
let highValuePurchases = c.Purchases.Where (p => p.Price > 1000)
where highValuePurchases.Any()
select new
{
c.Name,
highValuePurchases
}
If you do not know how Let working than please download LinqPad and see an example

C# LINQ sub-where

I have object
public class OrderItem
{
public string idProduct { get; set; }
public int quantity { get; set; }
public List<WarehouseItem> WarehouseInfo = new List<WarehouseItem>();
}
public class WarehouseItem
{
public string Name{ get; set; }
public string LocnCode{ get; set; }
}
and i need select items which have WarehouseInfo.LocnCode == "A1"
It is doesnt work when I use something like
var items = itemList.Where(x => x.WarehouseInfo.Where(y => y.LocnCode.Equals("A1")));
Your requirements could be interpreted one of three ways, so here's three solutions:
Give me all OrderItems where ANY WarehouseItem has a LocnCode of "A1":
var items = itemList.Where(i => i.WarehouseInfo.Any(w => w.LocnCode == "A1"));
Give me all WarehouseItems within the OrderItems that have a LocnCode of "A1":
var items = itemList.SelectMany(i => i.WarehouseInfo)
.Where(w => w.LocnCode.Equals("A1"));
Give me all OrderItems where ANY WarehouseItem has a LocnCode of "A1", and filter WarehouseInfo to only those WarehouseItems:
This can't be done in a simple Linq query because there's no way to change the contents of the existing objects. You're going to have to create new objects with the filtered values:
var items = itemList.Where(i => i.WarehouseInfo.Any(w => w.LocnCode == "A1"))
.Select(i => new OrderItem
{
idProduct = i.idProduct,
quantity = i.quantity,
WarehouseInfo = i.WarehouseInfo.Where(w => w.LocnCode.Equals("A1"));
.ToList()
}
);
Try
var items = itemList.Where(x => x.WarehouseInfo.Any(y => y.LocnCode.Equals("A1")));
The Where takes a predicate that should return a bool. Any will return true if at least one item in the collection returns true for the given predicate.

Categories