I'm trying to join 3 dataset into a List.
PERSONS (physical persons) :
var persons = await _context.Persons.ToListAsync();
USERS (useraccounts) :
var users = await _context.Users.ToListAsync();
WORKCONTRACTS :
I'm using a DTO here to lighten the data.
var workcontractsList = await _context.Workcontracts
.Select(c => new WorkcontractDto()
{
Id = c.Id,
PersonId = c.PersonId,
WorkcontractType = c.WorkcontractType,
StartDate = c.StartDate,
EndDate = c.EndDate
})
.ToListAsync();
Each Person may, or not, have a matching User.
Each Person may, or not, have one or more Workcontracts.
The result should be a list of PersonDto each containing a Person, her Username (coming from User) and a list of Workcontracts.
var query = await from p in persons
join u in users on p.Id equals u.PersonId
join w in workcontractsList on p.Id equals w.PersonId into wlist
select (p => new PersonDto()
{
PersonId = p.Id,
Person = p,
OrigamiUserName = u.UserName,
Workcontracts = wlist.ToList()
});
return query;
I get this error :
CS1941 : C# The type of one of the expressions in the join clause is incorrect. Type inference failed in the call to 'Join'.
I tried to remove on of the two joins without success. All the keys used in the joins are of the same type (int) and are enforced at the database level.
Please help.
SOLUTION FOUND :
After reading the comments, I had to admit my approach was wrong. Coming from an Data background, I tend to use "step by step" patterns. I rewrote the query and i works :
var query = await _context.Persons.Select(p => new PersonDto
{
PersonId = p.Id,
Person = p,
OrigamiUserName = p.OrigamiUser.UserName,
Workcontracts = p.Workcontracts.Select(c => new WorkcontractDto
{
Id = c.Id,
PersonId = c.PersonId,
WorkcontractType = c.WorkcontractType,
StartDate = c.StartDate,
EndDate = c.EndDate
}).ToList()
}).ToArrayAsync();
return query;
I guess I could still optimize it more, but, so far, it works as intended.
Thanks for the multiple heads up !
After reading the comments, I had to admit my approach was wrong. Coming from an Data background, I tend to use "step by step" patterns. I rewrote the query and i works :
var query = await _context.Persons.Select(p => new PersonDto
{
PersonId = p.Id,
Person = p,
OrigamiUserName = p.OrigamiUser.UserName,
Workcontracts = p.Workcontracts.Select(c => new WorkcontractDto
{
Id = c.Id,
PersonId = c.PersonId,
WorkcontractType = c.WorkcontractType,
StartDate = c.StartDate,
EndDate = c.EndDate
}).ToList()
}).ToArrayAsync();
return query;
I guess I could still optimize it more, but, so far, it works as intended.
Thanks for the multiple heads up !
Related
I trying to translate SQL Query to Linq statement:
SELECT f.BarcodeNumber,m.Name, f.Model, SUM(f.Quantity) as FoundedAssetsQty, ISNULL(a.Quantity,0) as AssetQty
FROM [InventoryDB].[dbo].[FoundedAssets] f
join [InventoryDB].[dbo].[PhisicalStockCheckSheets] p on p.ID = f.PhisicalStockCheckSheetId
join [InventoryDB].[dbo].[Inventories] i on i.ID = p.InventoryId
left join [InventoryDB].[dbo].[Assets] a on a.BarcodeNumber = f.BarcodeNumber
join [InventoryDB].[dbo].[Manufacturers] m on m.ID = f.ManufacturerId
where p.InventoryId = 10
group by f.BarcodeNumber, a.Quantity, f.Model, m.Name
I have no idea how to do it. I tried many ways but I fail. Could anyone help me?
I tried to use Linqer, but when I configure the connection it fails, so I write the linq instruction myself. Finally I found the answer. I have not mentioned the relations between entities but it is not important here.
var summary = _context.FoundedAssets.Include(f => f.Manufacturer).
Include(f => f.Asset).
Include(f => f.PhisicalStockCheckSheet).ThenInclude(f => f.Inventory).
Where(f => f.PhisicalStockCheckSheet.Inventory.ID == id).
Select(x => new InventorySummaryModel()
{
BarcodeNumber = x.BarcodeNumber.Value,
ManufacturerName = x.Manufacturer.Name,
Model = x.Model,
AssetsQuantity = x.Asset.Quantity,
FoundedAssetQuantity = x.Quantity
}).ToList();
var groupedSummary = summary.GroupBy(x => x.BarcodeNumber).Select(x => new InventorySummaryModel()
{
BarcodeNumber = x.First().BarcodeNumber,
ManufacturerName = x.First().ManufacturerName,
Model = x.First().Model,
FoundedAssetQuantity = x.Sum(a => a.FoundedAssetQuantity),
AssetsQuantity = x.First().AssetsQuantity
}).ToList();
Maybe exists any easier approach but this one works properly.
I have a webAPI that receives a list of objects. Currently this data is passed to a stored proc as a data table object, but as the dataDump can hold 1000's of ResponseAPI records, this is proving a little slow.
So, I have started to look into the concept of using EF to take the load, pushing all changes in one go using a SaveChanges on the context.
var dataDump = new List<ResponseAPI>
{
new ResponseAPI
{
Id= "1000",
Code = "C15",
value = "1976"
},
new ResponseAPI
{
Id = "999",
Code = "C14",
value = "1976"
}
};
var step2 = from l in dataDump
join p in Plants on new { X1 = l.Code } equals new { X1 = p.Code }
join ps in PlantTypes on new { X1 = l.Code, X2 = p.Id } equals new { X1 = ps.Code, X2= ps.Id}
where ps.TypeName == "Daisy"
select new {
Code = l.Code,
Id = p.Id
};
As far I can tell, this code is working, no errors are produced. What I am trying to obtain is a list of Id's from the dataDump that currently do not exist in the Plants table.
I have had some success using a different technique, but that only had the one table join.
var step1 = dataDump.Where(s =>
s.Code.Any(
a => !context.Plants
.Select(x => x.Code)
.Contains(s.Code)))
.Select(s => s.Code)
.Distinct()
.ToList();
The first snippet "step2" is just doing a basic join between the tables, which works, but I am not sure how to achieve the not! on the joins.
The second snippet "step1" has a not! on the context which does the job of only returning the values from the dataDump what are not in the Plants table.
My perferred method would be step1, but I do not how to add the second join which links on two fields.
var step1 = dataDump.Where(s =>
s.Code.Any(a => !context.Plants.Select(x => x.Code).Contains(s.Code))
&&
s.Code.Any(a => !context.Plants.any(x => x.PlantTypes.Select(t => t.Code).Contains(s.Code)))
).Select(s => s.Code).Distinct().ToList();
This might need some fix but I would like to know if it's really a one to many to many relationship between your entities.
I am trying to translate my query syntax into Lambda and I have looked at a couple of threads here, some of which:
C# Linq Lambda Left Outer Join
How do you perform a left outer join using linq extension methods
And I still dont understand exactly how to implement what I want.
var suppliersAndBuyers = from s in suppliers
orderby s.District
join b in buyers on s.District equals b.District into buyersGroup
select new
{
SupplierName = s.Name,
s.District,
Buyers = buyersGroup.DefaultIfEmpty(
new Buyer()
{
Name = "No one here",
District = "I dont exist",
})
};
This is my linq query and it works perfectly, each of suppliers has its own group of buyers, and those suppliers that dont have buyers, simply get the default Buyer object that I am creating in the DefaultIfEmpty method.
I tried doing the following with Method Syntax:
var leftOuterJoinBuyer = suppliers.GroupJoin(
buyers,
s => s.District,
b => b.District,
(s, buyersGroup) => new
{
s.Name,
s.District,
Buyers = buyersGroup
})
.SelectMany(s => s.Buyers.DefaultIfEmpty(new Buyer() { Name = "No name", District = "No place", }),
(s, b) => new
{
SupplierName = s.Name,
Buyers = s.Buyers,
s.District
}
);
And both approaches return the exact same types, but I dont get the default object for my no match suppliers, and those that have matches get added several times to the end result.
For example Harrison gets added 3 times to the final collection with the exact same collection of buyers, basicly 3 identical results that are not needed and are not produced in my query syntax. Screenshot -> https://prntsc/iocsby
What is the correct way to rewrite this query?
This is the lambda version:
var lambdaQuery = suppliers.GroupJoin(buyers, s => s.District, b => b.District,
(s, buyersGroup) => new {
SupplierName = s.Name,
s.District,
Buyers = buyersGroup.DefaultIfEmpty(new Buyer() { Name = "No one here", District = "I don't exist" })
});
I have this simple code that returns a list of products but now I need to somehow fetch the same lis of products BUT i need to add a new column or value based on a view count.
var products = db.Products.Where(p => p.ProductOwnerUserId == userID).OrderByDescending(p => p.ProductID);
this is what i have so far but i am no expert in LINQ so i was wondering if someone could help me here.
This is a kind of pseudo-code of what i am looking for
var products = from p in db.Products
join pr in db.Reviews on p.ProductID equals pr.ReviewForProductID
into g select new
{
p.*,
ProductView = g.Count(a => a.ReviewForProductID)
};
i have found my OWN answer since nothing came up from you guys... but thanx for the initial tips... im quite new with linq and complexe queries can be hard to understand and fit inside existing code/view
here is my solution:
Thank you for your first answer and well just too bad for the second one that NEVER came... FYI, since my product class is a partial class already a just added another new ProductView.cs partial class containg the new Property and my query (functionnal and tested) looks like this now:
var products = (from p in db.Products
join pr in db.Reviews on p.ProductID equals pr.ReviewForProductID
into g
select new GenericEcomDataAccess.Product
{
ProductID = p.ProductID,
ProductOwnerUserId = p.ProductOwnerUserId,
ProductCurrency = p.ProductCurrency,
ProductDescription = p.ProductDescription,
ProductPrice = p.ProductPrice,
ProductImage = p.ProductImage,
ProductName = p.ProductName,
ProductCount = g.Count()
}).Where(p => p.ProductOwnerUserId == userID)
.OrderByDescending(p => p.ProductID).AsEnumerable();
var products = db.Products.Where(p => p.ProductOwnerUserId == userID)
.OrderByDescending(p => p.ProductID)
.Select(p=> new {Product = p, Count = p.Reviews.Count()});
If you have the foreign keys set up properly
I am trying to perform a Join between multiple tables in LINQ. I have the following classes:
Product {Id, ProdName, ProdQty}
Category {Id, CatName}
ProductCategory{ProdId, CatId} //association table
And I use the following code (where product, category and productcategory are instances of the above classes):
var query = product.Join(productcategory, p => p.Id, pc => pc.ProdID, (p, pc) => new {product = p, productcategory = pc})
.Join(category, ppc => ppc.productcategory.CatId, c => c.Id, (ppc, c) => new { productproductcategory = ppc, category = c});
With this code I obtain an object from the following class:
QueryClass { productproductcategory, category}
Where producproductcategory is of type:
ProductProductCategoryClass {product, productcategory}
I do not understand where the joined "table" is, I was expecting a single class that contains all the properties from the involved classes.
My aim is to populate another object with some properties resulting from the query:
CategorizedProducts catProducts = query.Select(m => new { m.ProdId = ???, m.CatId = ???, //other assignments });
how can I achieve this goal?
For joins, I strongly prefer query-syntax for all the details that are happily hidden (not the least of which are the transparent identifiers involved with the intermediate projections along the way that are apparent in the dot-syntax equivalent). However, you asked regarding Lambdas which I think you have everything you need - you just need to put it all together.
var categorizedProducts = product
.Join(productcategory, p => p.Id, pc => pc.ProdId, (p, pc) => new { p, pc })
.Join(category, ppc => ppc.pc.CatId, c => c.Id, (ppc, c) => new { ppc, c })
.Select(m => new {
ProdId = m.ppc.p.Id, // or m.ppc.pc.ProdId
CatId = m.c.CatId
// other assignments
});
If you need to, you can save the join into a local variable and reuse it later, however lacking other details to the contrary, I see no reason to introduce the local variable.
Also, you could throw the Select into the last lambda of the second Join (again, provided there are no other operations that depend on the join results) which would give:
var categorizedProducts = product
.Join(productcategory, p => p.Id, pc => pc.ProdId, (p, pc) => new { p, pc })
.Join(category, ppc => ppc.pc.CatId, c => c.Id, (ppc, c) => new {
ProdId = ppc.p.Id, // or ppc.pc.ProdId
CatId = c.CatId
// other assignments
});
...and making a last attempt to sell you on query syntax, this would look like this:
var categorizedProducts =
from p in product
join pc in productcategory on p.Id equals pc.ProdId
join c in category on pc.CatId equals c.Id
select new {
ProdId = p.Id, // or pc.ProdId
CatId = c.CatId
// other assignments
};
Your hands may be tied on whether query-syntax is available. I know some shops have such mandates - often based on the notion that query-syntax is somewhat more limited than dot-syntax. There are other reasons, like "why should I learn a second syntax if I can do everything and more in dot-syntax?" As this last part shows - there are details that query-syntax hides that can make it well worth embracing with the improvement to readability it brings: all those intermediate projections and identifiers you have to cook-up are happily not front-and-center-stage in the query-syntax version - they are background fluff. Off my soap-box now - anyhow, thanks for the question. :)
What you've seen is what you get - and it's exactly what you asked for, here:
(ppc, c) => new { productproductcategory = ppc, category = c}
That's a lambda expression returning an anonymous type with those two properties.
In your CategorizedProducts, you just need to go via those properties:
CategorizedProducts catProducts = query.Select(
m => new {
ProdId = m.productproductcategory.product.Id,
CatId = m.category.CatId,
// other assignments
});
take look at this sample code from my project
public static IList<Letter> GetDepartmentLettersLinq(int departmentId)
{
IEnumerable<Letter> allDepartmentLetters =
from allLetter in LetterService.GetAllLetters()
join allUser in UserService.GetAllUsers() on allLetter.EmployeeID equals allUser.ID into usersGroup
from user in usersGroup.DefaultIfEmpty()// here is the tricky part
join allDepartment in DepartmentService.GetAllDepartments() on user.DepartmentID equals allDepartment.ID
where allDepartment.ID == departmentId
select allLetter;
return allDepartmentLetters.ToArray();
}
in this code I joined 3 tables and I spited join condition from where clause
note: the Services classes are just warped(encapsulate) the database operations
public ActionResult Index()
{
List<CustomerOrder_Result> obj = new List<CustomerOrder_Result>();
var orderlist = (from a in db.OrderMasters
join b in db.Customers on a.CustomerId equals b.Id
join c in db.CustomerAddresses on b.Id equals c.CustomerId
where a.Status == "Pending"
select new
{
Customername = b.Customername,
Phone = b.Phone,
OrderId = a.OrderId,
OrderDate = a.OrderDate,
NoOfItems = a.NoOfItems,
Order_amt = a.Order_amt,
dis_amt = a.Dis_amt,
net_amt = a.Net_amt,
status=a.Status,
address = c.address,
City = c.City,
State = c.State,
Pin = c.Pin
}) ;
foreach (var item in orderlist)
{
CustomerOrder_Result clr = new CustomerOrder_Result();
clr.Customername=item.Customername;
clr.Phone = item.Phone;
clr.OrderId = item.OrderId;
clr.OrderDate = item.OrderDate;
clr.NoOfItems = item.NoOfItems;
clr.Order_amt = item.Order_amt;
clr.net_amt = item.net_amt;
clr.address = item.address;
clr.City = item.City;
clr.State = item.State;
clr.Pin = item.Pin;
clr.status = item.status;
obj.Add(clr);
}
var query = from a in d.tbl_Usuarios
from b in d.tblComidaPreferidas
from c in d.tblLugarNacimientoes
select new
{
_nombre = a.Nombre,
_comida = b.ComidaPreferida,
_lNacimiento = c.Ciudad
};
foreach (var i in query)
{
Console.WriteLine($"{i._nombre } le gusta {i._comida} y naciĆ³ en {i._lNacimiento}");
}
it has been a while but my answer may help someone:
if you already defined the relation properly you can use this:
var res = query.Products.Select(m => new
{
productID = product.Id,
categoryID = m.ProductCategory.Select(s => s.Category.ID).ToList(),
}).ToList();