I have a Personnel table and PersonnelDrivingLicense table. There is more than one record in the PersonnelDrivingLicense table related to one Personnel.
I tried to get Personnel and PersonnelDrivingLicense data with linq but I am getting 2 record instead 1.
Here is my linq query:
from p in Personnel
join pdl in PersonnelDrivingLicense on p.Id equals pdl.PersonnelId
select new Personnel
{
Id = p.Id,
PersonnelDrivingLicense = new List<PersonnelDrivingLicense>
{
new PersonnelDrivinLicense
{
Id = pdl.Id,
DrivingLicenseClass = pdl.DrivingLicenseClass
}
}
}
This linq returns below result:
Id: 1,
PersonnelDrivingLicense:
Id: 1,
DrivingLicenseClass: B
Id: 1,
PersonnelDrivingLicense:
Id: 2,
DrivingLicenseClass: C
The correct result should be below:
Id: 1,
PersonnelDrivingLicense:
Id: 1,
DrivingLicenseClass: B
Id: 2,
DrivingLicenseClass: C
How can I get desired result above?
How should I write correct linq query?
Thanks.
You should group the returned Personnel objects by Id. Try this:
from p in Personnel
join pdl in PersonnelDrivingLicense on p.Id equals pdl.PersonnelId
group p by p.Id into g
select new Personnel
{
Id = g.Key,
PersonnelDrivingLicense = g.Select(x => x.PersonnelDrivingLicense).ToList()
}
If you have a one-to-many or a many-to-many relationship, and you want an item with their sub-items, you should use GroupJoin instead of Join.
In you case, you have Personnel and PersonnelDrivingLicenses, probably a one-to-many relation: every Personnel object has zero or more PersonnelDrivingLicences. Every PersonnelDrivingLicence belongs to exactly one Personnel object, namely the Personnel that the foreign key PersonnelId refers to.
Apparently you want a sequence of Personnel objects, each Personnel object with a list of his/her PersonnelDrivingLicenses.
// GroupJoin Personnel with PersonnelDrivingLicenses
var personnelWithTheirDrivingLicenses = myDbContext.Personnel
.GroupJoin(myDbDcontext.PersonnelDrivingLicenses,
// from every personnel object take the Id,
personnel => personnel.Id,
// from every driving license take the PersonnelId
personnelDrivingLicence => personnelDrivingLicence.PersonnelId,
// Take the Personnel, with all his matching driving licenses to make a new:
(personnel, drivingLicenses) => new
{
// for optimal efficiency, Select only the properties you plan to use:
Id = personnel.Id,
Name = personnel.Name,
...
DrivingLicenses = drivingLicenses
.Where(drivingLicense => ...) // only if you don't want all his DrivingLicenses
.Select(drivingLicense => new
{
// again, select only the properties you plan to use:
Id = drivingLicense.Id,
Type = drivingLicense.Type,
...
// not needed, you already know the value:
// PersonnelId = drivingLicense.PersonnelId,
})
.ToList(),
});
Related
I have a situation where I have a LINQ query. It has two joins (one to many) but it is bringing back all of the columns in the joined tables. I'm not sure how to create the LINQ query to only being back a few fields from the joined tables.
var data = from mc in ctx.MembershipChapters
where mc.PartitionKey == controllerStateManager.PartitionKey && mc.MembershipId == membershipId
join prd in ctx.Products on mc.ProductId
equals prd.Id into prods
from prd in prods.DefaultIfEmpty()
join oli in ctx.OrderLineItems on mc.OrderLineItemId equals oli.OrderLineItemId into olis
from oli in olis.DefaultIfEmpty()
select new
{
MembershipName = mc.Membership.Name,
Products = prods.Select(p => new {
ProductName = p.Name, ProductId = p.Id }),
OrderLineItems = olis.Select(o => new { OrderLineItemName = o.Description, OrderLineItemId = o.OrderLineItemId })
};
controllerStateManager.Data = data.ToList();
This does not work...I get an error: "o" is not in scope.
Basically the output should follow this:
MembershipChapter
---> OrderLineItems
----------> Products
I'm new to LINQ and I have been struggling on this for far too long.
If you have a one-to-many relationship, and you want the "one" items, each one with its zero or more subitems, like Schools with their zero or more Students; Customers with their zero or more Orders, or, as in your case: MembershipChapters with their OrderLineItems, consider to use one of the overloads of Queryable.GroupJoin.
If you start on the "many" side, and you want each item with its one parent item, so you want the Student with the School he attends, or the Order with the one and only Customer who placed the order, use one of the overloads of Queryable.Join.
I almost always use the overload that has a parameter resultSelector, so you can exactly define what should be in the result.
Requirement: given tables MembershipChapters, OrderLineItems and Products. There is a one-to-many relationship between MembershipChapters and OrderLineItems. Every MembershipChapter has zero or more OrderLineItems, every OrderLineItem belongs to exactly one MembershipChapter, namely the MembershipChapter that the foreign key refers to. There is a similar one to many relation between OrderLineItems and Products. Give me all (or some) MembershipChapters, each MembershipChapter with its zero or more OrderlineItems, and each OrderLineItem with its zero or more Products.
var result = dbContext.MemberShipChapters
.Where(membershipChapter => ...) // only if you don't want all MembershipChapters
.GroupJoin(dbContext.OrderLineItems,
membershipChapter => membershipChapter.Id, // from every membershipChapter get the primary key
orderlineItem => orderLineItem.MembershipChapterId, // from every OrderlineItem get the foreign key
// parameter resultSelector: from every MembershipChapter with its zero or more
// OrderLineItems, make one new:
(membershipChapter, orderLineItemsOfThisMembershipChapter) => new
{
// Select only the membershipChapter properties that you plan to use
Id = membershipChapter.Id,
Name = membershipChapter.Name,
...
// The zero or more OrderLineItems of this membershipChapter
OrderLineItems = orderLineItemsOfThisMembershipChapter
.Select(orderLineItem => new
{
// Select only the OrderLineItems that you plan to use:
Id = orderLineItem.Id,
...
// not needed, you already know the value
// MembershipChapterId = orderLineItem.MembershipChapterId,
})
.ToList(),
});
This is fairly straightforward. However, if you want to GroupJoin three tables, then this looks horrible, although it is doable.
Another method that looks simpler:
var result = dbContext.MemberShipChapters
.Where(membershipChapter => ...)
.Select((membershipChapter => new
{
Id = membershipChapter.Id,
Name = membershipChapter.Name,
...
OrderLineItems = dbContext.OrderLineItems
// keep only the OrderLineItems with a foreign key referring to this MembershipChapter
.Where(orderLineItem => orderLineItem.MemberShipChapterId == membershipChapter.Id)
.Select(orderLineItem => new
{
Id = orderLineItem.Id,
...
// do the same with the third table
Products = dbContext.Products
.Where(product => product.OrderLineItemId == orderLineItem.Id)
.Select(product => new
{
Id = product.Id,
Price = product.Price,
...
})
.ToList(),
})
.ToList(),
});
It is a little hard to read, but if the domain is linked correctly then I think you just want to end up with a query like this:
from ol in ctx.OrderLines where
ol.MembershipChapter.PartitionKey == controllerStateManager.PartitionKey
select new {ol.Whatever, ol.Product.Whatever};
I'm developing a web application using Entity Framework.
I need do a select and pass values for an Ilist but it's returns duplicate values.
IQueryable<establishmentInfo> filter = (from x in db.establishments
join t in db.establishment_categories on x.id equals t.establishment
join q in db.categories on t.category equals q.id
where (x.name.ToUpper().Contains(search.ToUpper()))
select new establishmentInfo
{
id = x.id,
name = x.name,
id_category = q.id,
category = q.name,
});
IList<establishmentInfo>establishments = filter.ToList();
Establishment table
id name email
---------------------------
1 AAA a#a.com
2 BBB b#b.com
Establishment_categories
id establishment category
-------------------------------
1 1 1
2 1 2
3 2 1
Categories
id name
---------------------
1 alpha
2 beta
The problem is that return 2 establishments, one with category 1 and other with category 2. I need remove one of these.
Can anyone help?
As #NetMage said,your linq statement should return two values that are not repeated.
We can see that there are two records with establishment set to 1 in your Establishment_categories table. You can check your establishments. The id_category should be 1, the category should be alpha, the other should be id_categoryis 2, and the category should be beta.
You can see below image:
If you only want to get the first data of establishments, you can write the code as follows:
IQueryable<Establishment> filter = (from x in _context.Establishments
join t in _context.Establishment_Categories on x.Id equals t.EstablishmentId
join q in _context.Categories on t.CategoryId equals q.Id
where x.Name.ToUpper().Contains(search.ToUpper())
select new Establishment
{
Id = x.Id,
Name = x.Name,
CategoryId = q.Id,
CategoryName = q.Name,
}).Take(1);
List<Establishment> establishments = filter.ToList();
Result:
By the way, assuming that there are duplicates in your returned data, you can add the .Distinct() method after your linq to remove duplicates.
Please Consider these 2 tables:
CategoryID CategoryName CategoryModel
-----------------------------------------------------------
1 Book 1
2 Shoe 2
3 Glass 1
and
SubCategoryID SubCategoryName CategoryID SubCategoryModel OtherColumn1 OtherColum2
---------------------------------------------------------------------
1 Book1 1 3
2 Book2 1 1
3 Shoe1 2 2
4 Shoe2 2 2
I want such this query:
from a in Category
join b in SubCategory
on a.CategoryID equals b.CategoryID into grpDetail
where a.CategoryModel != b.SubCategoryModel <----------
select new
{
Id = a.CategoryID,
Count1 = grpDetail.Count(o=>o.OtherColumn1 == 1),
...
}
the problem id I can't access to b in above specifies line. How can I write this query?
Thanks
There is a straightforward one to many relation between Categories and SubCategories: every Category has zero or more SubCategories; every SubCategory belongs to exactly one Category, namely the Category that the foreign key SubCategory.CategoryId refers to.
You want to join Category and SubCategory on this foreign key. You don't want all Category-SubCategory combinations that match, you want only those where Category.CategoryModel is not equal to SubCategory.SubCategoryModel.
From the remaining records, you want to select several properties. I don't see property GrpDetail in your classes, so I don't know what you want.
Luckily you mention that your problem is in the Where:
var result = Categories.Join(SubCategories, // join tables Categories and SubCategories
category => category.Id, // from every category take the Id,
subCategory => subCategory.CategoryId, // from every subCategory take foreign key CategoryId
(category, subCategory) => new // when they match make one new object
{
// we need at least Category.CategoryModel and SubCategory.SubCategoryModel
CategoryModel = category.CategoryModel,
SubCategoryModel = subCategory.SubCategoryModel,
// Select other Category properties that you plan to use:
CategoryId = category.Id,
...
// Select other SubCategory properties that you plan to use:
...
})
// we don't want all combinations, only those where
// CategoryModel is not equal to SubCategoryModel
.Where(joinResult => joinResult.CategoryModel != joinResult.SubCategoryModel)
// from the remaining combinations calculate the final result
.Select(joinResult => new
{
Id = joinResult.CategoryId,
Count1 = ... // sorry, don't know what property grpDetail does
...
});
split your query into 2, first do your join with the where clause and then do your group by.
I have two list like
List<Customer> customer = new List<Customer>()
{ Id =1 , Name = 'Demo1' , OrderId = 123}
{ Id =1 , Name = 'Demo2' , OrderId = 123}
List<Order> order = new List<Order>()
{ Id =77 , CustomerName = 'Demo1'}
{ Id =88 , CustomerName = 'Demo2'}
I want to replace customer.OrderId = order.Id where order.CustomerName = customer.Name
I want to replace customer list OrderId value from order list when CustomerName is matching with Name prop
I was trying something like this -
customer = order.Select(eo => new Customer { Name = eo.CustomerName });
this is not correct LINQ can anyone correct me here?
LINQ is primarily suited for querying, not data modification.
Instead, I would just use simple foreach:
foreach (var c in customer)
{
var o = order.FirstOrDefault(o => o.CustomerName == c.Name);
c.OrderId = o?.Id ?? 0;
}
Of course this approach will not work as well when there are multiple orders per customer. Also I would advise to rename the variables to plural - customers and orders to better denote their meaning.
For a purely LINQ approach, you could write a ForEach LINQ extension method, but I find an explicit foreach a more readable solution.
LINQ is primarily used for querying. You can make a new list that matches your requirements.
If desired you can assign this new list to your variable customers.
You want to join your customers and orders on the name of the customer.
Simple solution:
var joinResult = customers.Join(orders, // join the tables of customers with orders
customer => customer.Name, // from every customer take the Name
order => order.CustomerName, // from every order take the CustomerName
(customer, order) => new Customer // when they match make a new Customer
{
Id = customer.Id, // take Id and Name from the matching Customer
Name = customer.Name,
OrderId = order.Id, // take the OrderId from the matching order
})
.ToList();
customers = joinResult;
Alas, this won't work if you have a Customer with several Orders:
var customers = new List<Customer>()
{ Id = 1 , Name = 'John Doe' , OrderId = 123},
var orders = new List<Order>()
{ Id =77 , CustomerName = 'John Doe'}
{ Id =88 , CustomerName = 'John Doe'}
Should Customer 1 have OrderId 77 or 78?
Are you sure that every Customer has only one Order?
To get a Customer with all his Orders use GroupJoin
var result = customers.GroupJoin(orders, // GroupJoin the customers with orders
customer => customer.Name, // from every customer take the Name
order => order.CustomerName, // from every order take the CustomerName
(customer, orders) => new // for every customer with all his matching orders
{ // make one new object
Id = customer.Id, // take Id and Name from the matching Customer
Name = customer.Name,
// TODO Decide what to do if there are several orders for customer with this name
// Keep all orders? Or keep the oldest one, the newest one?
// the unpaid ones?
AllOrders = orders.ToList(),
OldestOrder = orders.Orderby(order => order.Date).FirstOrDefault(),
NewestOrder = orders.OrderByDescending(order => order.Date).FirstOrDefault(),
UnpaidOrders = orders.Where(order => order.Status == Status.Unpaid).ToList(),
})
.ToList();
You want to perform a join operation (most likely an inner join). LINQ provides such a feature
var customerOrders = customer.Join(order,
c => c.Name,
o => o.CustomerName,
(customer, order) =>
{
custumer.OrderId= order.Id;
return customer;
}).ToList();
But as #Martin Zikmund says i'd be carefull with manipulation the data directly.
You need to join both lists on property Name from customer and property CustomerName from order and then assign OrderId from Order like
List<Customer> result = new List<Customer>();
result = (from c in customer
join o in order on c.Name equals o.CustomerName
select new Customer
{
Id = c.Id,
Name = c.Name,
OrderId = o.Id
}).ToList();
foreach (var item in result)
{
Console.WriteLine($"Id: {item.Id}, \t Name: {item.Name}, \t OrderId: {item.OrderId}");
}
Console.ReadLine();
Output:
You can loop through pairs of customer and correspondent orders and update customers for only matched pairs.
var matched = customers.Join(orders,
customer => customer.Name,
order => order.CustomerName,
(customer, order) => (Customer: customer, Order: order));
foreach (var pair in matched)
{
pair.Customer.OrderId = pair.Order.Id;
}
Notice that in case when order collection contains more than one order with same customer name, Join approach will update customer with order Id occurred last in the collection.
LINQ extension methods designed in "functional" way, where enumerated items handled as immutable. LINQ methods always return new instance of the collection. Most of the developers will be "very" surprised if items will be mutated during enumeration methods.
So having explicit foreach loop will clearly tell other developers of your intentions.
I have two tables. Table One contains a list of Areas, and Table Two contains a list of Samples, with each Sample row containing Area_ID as a Foreign Key.
I need to retrieve all the records in my Area table with only the most recent corresponding Sample Status. I have this query, but it just returns one Area with the most recent sample from the Sample table:
var result = (
from a in db.area
join c in db.sample
on a.location_id equals c.location_id
select new
{
name = a.location_name,
status = c.sample_status,
date = c.sample_date
}).OrderByDescending(c => c.date).FirstOrDefault();
A solution could be filtering your second DbSet:
var result = from a in db.area
join c in db.sample.Where(s=>s.location_id==a.location_id).OrderByDescending(c => c.sample_date).Take(1)
on a.location_id equals c.location_id
select new
{
name = a.location_name,
status = c.sample_status,
date = c.sample_date
};
Another solution could be applying a group join:
var result = from a in db.area
join c in db.sample
on a.location_id equals c.location_id into samples
let sample=samples.OrderByDescending(c => c.sample_date).FirstOrDefault()
select new
{
name = a.location_name,
status = sample.sample_status,
date = sample.sample_date
};
If you use navigation properties could be even easier. Supposing you have a one to many relationship between Area and Sample:
var result =from a in db.area
let sample= a.Samples.OrderByDescending(c => c.sample_date).FirstOrDefault()
select new
{
name = a.location_name,
status = sample.sample_status,
date = sample.sample_date
};