left join two tables in same DataContext LinQ in c# - linq

I have two table in the same DataContext as follows.
Table PersonnelInfo
{
personnelId,
personnelName ,
description,
deathMonthYear,
updatedBy,
updatedAt
}
Table PersonnelInfoOther
{
personnelId,
personnelName ,
updatedBy,
updatedAt
}
I define a class as follows:
public class PersonnelInfoAll
{
public short personnelId{get;set;}
public string personnelName { get; set; }
public string personnelNameOtherLan { get; set; }
public string description { get; set; }
public string deathMonthYear { get; set; }
public int updatedBy { get; set; }
public DateTime updatedAt { get; set; }
}
I need to left join first table with the second one and retrieve all the data as PersonnelInfoAll format:
public List<PersonnelInfoAllLan> GetPersonnelInfosAll()
{
var context = new BookDataClassesDataContext { ObjectTrackingEnabled = false };
var personnelInfo = from u in context.PersonnelInfos
join b in context.PersonnelInfoOtherLans
on u.personnelId equals b.personnelId
select new PersonnelInfoAllLan
{
personnelId = u.personnelId,
personnelName = u.personnelName,
personnelNameOtherLan = b.personnelName,
description = u.description,
deathMonthYear = u.deathMonthYear,
updatedBy = u.updatedBy,
updatedAt = u.updatedAt
};
return personnelInfo.ToList();
}
This gives me only one row which matches with both. But I need all the records from the first table. Is there any one to help.

Use group join:
var personnelInfo = from p in context.PersonnelInfos
join l in context.PersonnelInfoOtherLans
on p.personnelId equals l.personnelId into g
from l in g.DefaultIfEmpty()
select new PersonnelInfoAllLan
{
personnelId = p.personnelId,
personnelName = p.personnelName,
personnelNameOtherLan = (l == null ? null : l.personnelName),
description = p.description,
deathMonthYear = p.deathMonthYear,
updatedBy = p.updatedBy,
updatedAt = p.updatedAt
};
If there no match in lans for some person, then DefaultIfEmpty() will return null from joined group. That's why you need to check l for null.

Related

product sale qty sum and name in linq group query

I have a product sales data and want to show the summary of sale grouped by product id.
Summary result should show product name and total sales. How can I select a field along with groupby result and that field is not the key field.
public partial class SaleOrderDetail
{
public int Id { get; set; }
public int ProductId { get; set; }
public string ProductName { get; set; }
public int Quantity { get; set; }
public decimal Price { get; set; }
public decimal LineTotal { get; set; }
}
var query = from saleorder in _dbContext.SaleOrderDetail
group saleorder by saleorder.ProductId into salesummary
select new
{
productid = salesummary.Key,
prdouctname = salesummary.First().ProductName,
totalqty = salesummary.Sum(s => s.Quantity)
};
I got the error invalidoperationException because of First() for product name.
You have to include ProductName in grouping Key.
var query =
from saleorder in _dbContext.SaleOrderDetail
group saleorder by new {saleorder.ProductId, saleorder.ProductName} into salesummary
select new
{
productid = salesummary.Key.ProductId,
prdouctname = salesummary.Key.ProductName,
totalqty = salesummary.Sum(s => s.Quantity)
};
Making SaleOrderDetail as AsEnumerable() did the trick. For SQL Expression it will work if make it as AsEnumerable() or .ToList<> etc.
var query = from saleorder in _dbContext.SaleOrderDetail.AsEnumerable()
group saleorder by saleorder.ProductId into salesummary
select new
{
productid = salesummary.Key,
prdouctname = salesummary.First().ProductName,
totalqty = salesummary.Sum(s => s.Quantity)
};

EF Core - many queries sent to database for subquery

Using EF Core 2.2.2, I have a table in my database which is used to store notes for many other tables. In other words, it's sortof like a detail table in a master-detail relationship, but with multiple master tables. Consider this simplified EF Model:
public class Person
{
public Guid PersonID { get; set; }
public string Name { set; set; }
}
public class InvoiceItem
{
public Guid InvoiceItemID { get; set; }
public Guid InvoiceID { get; set; }
public string Description { get; set; }
}
public class Invoice
{
public Guid InvoiceID { get; set; }
public int InvoiceNumber { get; set; }
public List<Item> Items { get; set; }
}
public class Notes
{
public Guid NoteID { get; set; }
public Guid NoteParentID { get; set; }
public DateTime NoteDate { get; set; }
public string Note { get; set; }
}
In this case, Notes can store Person notes or Invoice notes (or InvoiceItem notes, though let's just say that the UI doesn't support that).
I have query methods set up like this:
public IQueryable<PersonDTO> GetPersonQuery()
{
return from p in Context.People
select new PersonDTO
{
PersonID = p.PersonID,
Name = p.Name
};
}
public List<PersonDTO> GetPeople()
{
return (from p in GetPersonQuery()
return p).ToList();
}
public IQueryable<InvoiceDTO> GetInvoiceQuery()
{
return from p in Context.Invoices
select new InvoiceDTO
{
InvoiceID = p.InvoiceID,
InvoiceNumber = p.InvoiceNumber
};
}
public List<InvoiceDTO> GetInvoices()
{
return (from i in GetInvoiceQuery()
return i).ToList();
}
These all work as expected. Now, let's say I add InvoiceItems to the Invoice query, like this:
public IQueryable<InvoiceDTO> GetInvoiceQuery()
{
return from p in Context.Invoices
select new InvoiceDTO
{
InvoiceID = p.InvoiceID,
InvoiceNumber = p.InvoiceNumber,
Items = (from ii in p.Items
select new ItemDTO
{
ItemID = ii.ItemID,
Description = ii.Description
}).ToList()
};
}
That also works great, and issues just a couple queries. However, the following:
public IQueryable<InvoiceDTO> GetInvoiceQuery()
{
return from p in Context.Invoices
select new InvoiceDTO
{
InvoiceID = p.InvoiceID,
InvoiceNumber = p.InvoiceNumber,
Items = (from ii in p.Items
select new ItemDTO
{
ItemID = ii.ItemID,
Description = ii.Description
}).ToList(),
Notes = (from n in Context.Notes
where i.InvoiceID = n.NoteParentID
select new NoteDTO
{
NoteID = n.NoteID,
Note = n.Note
}).ToList(),
};
}
sends a separate query to the Note table for each Invoice row in the Invoice table. So, if there are 1,000 invoices in the Invoice table, this is sending something like 1,001 queries to the database.
It appears that the Items subquery does not have the same issue because there is an explicit relationship between Invoices and Items, whereas there isn't a specific relationship between Invoices and Notes (because not all notes are related to invoices).
Is there a way to rewrite that final query, such that it will not send a separate note query for every invoice in the table?
The problem is indeed the correlated subquery versus collection navigation property. EF Core query translator still has issues processing such subqueries, which are in fact logical collection navigation properties and should have been processed in a similar fashion.
Interestingly, simulating collection navigation property with intermediate projection (let operator in LINQ query syntax) seems to fix the issue:
var query =
from i in Context.Invoices
let i_Notes = Context.Notes.Where(n => i.InvoiceID == n.NoteParentID) // <--
select new InvoiceDTO
{
InvoiceID = i.InvoiceID,
InvoiceNumber = i.InvoiceNumber,
Items = (from ii in i.Items
select new ItemDTO
{
ItemID = ii.ItemID,
Description = ii.Description
}).ToList(),
Notes = (from n in i_Notes // <--
select new NoteDTO
{
NoteID = n.NoteID,
Note = n.Note
}).ToList(),
};

How can I use be generic to result of linq query?

Be first, my English is not very good. So I am sorry about that. :)
My question as the title. I have two tables on my database:
Siniflar (SinifId, SinifAdi, Kapasite, OgretmenId)
Ogretmenler (OgretmenId, Ad, Soyad, Brans)
Query:
var siniflar = (from s in db.Siniflar
join o in db.Ogretmenler
on s.OgretmenId equals o.OgretmenId
select new { s.SinifId, s.SinifAdi, s.Kapasite, o.Ad }).ToList();
I want to use the result of the query as a generic, like this:
public List<Siniflar> SiniflariListele()
{
var siniflar = (from s in db.Siniflar
join o in db.Ogretmenler
on s.OgretmenId equals o.OgretmenId
select new { s.SinifId, s.SinifAdi, s.Kapasite, o.Ad }).ToList();
return siniflar;
}
But I get an error. Because the result is anonymous types.
Error: Cannot implicitly convert type System.Collections.Generic.List<AnonymousType#1> to System.Collections.Generic.List<Entity.Siniflar>
How Can I use be generic result. What Can I do for that?
I changed question:
Class:
public class RSiniflar
{
public int SinifId { get; set; }
public string SinifAdi { get; set; }
public int Kapasite { get; set; }
public string OgretmenAdiSoyadi { get; set; }
}
Metod:
public List<RSiniflar> SiniflariListele()
{
List<RSiniflar> siniflar = (from s in db.Siniflar
join o in db.Ogretmenler
on s.OgretmenId equals o.OgretmenId
select new RSiniflar
{
SinifId = s.SinifId,
SinifAdi = s.SinifAdi,
Kapasite = s.Kapasite,
OgretmenAdiSoyadi = o.Ad + ' ' + o.Soyad
}).ToList();
return siniflar;
}
But now error to query: Unable to create a constant value of type 'System.Object'. Only primitive types or enumeration types are supported in this context.
I think, there are a problem on my query... I get an error same when query to var type...
You can return typed list, but you can't return a list of anonymous type. So you need to create a class which will handle your results:
public class A
{
public int SinifId { get; set; }
public int SiniAdi { get; set; }
public int Kapasite { get; set; }
public int Ad { get; set; }
}
And then use it in your query, instead of anonymous objects:
public List<A> SiniflariListele()
{
var siniflar = (from s in db.Siniflar
join o in db.Ogretmenler
on s.OgretmenId equals o.OgretmenId
select new A {
SinifId = s.SinifId,
SinifAdi = s.SinifAdi,
Kapasite = s.Kapasite,
Ad = o.Ad
}).ToList();
return siniflar;
}
You could do the same with an existing class (e.g. Siniflar), but you have to be sure that this class has all necessary properties.
Rather than using new { property1, property2 }, create new Siniflar's in your select.
E.g.
select new Siniflar() {
SinifId = s.SinifId,
SinifAdi = s.SinifAdi,
Kapasite = s.Kapasite }).ToList()
Note: as lazyberezovsky mentioned, your mapping seems to be flawed, as Siniflar does not contain the property Ad.

Get Count from entity framework

Quite new to EF, basically i want to convert this SQL query:
SELECT
PSKU.ProductSKUID,
PSKU.ProductSKUName,
W.WarehouseID,
W.WarehouseName,
SA.SystemAreaName,
COUNT(SLI.ProductSKUID) AS QTY
FROM dbo.StockLineItem AS SLI INNER JOIN
dbo.ProductSKU AS PSKU ON PSKU.ProductSKUID = SLI.ProductSKUID INNER JOIN
dbo.Warehouse AS W ON W.WarehouseID = SLI.WarehouseID INNER JOIN
dbo.SystemArea AS SA ON SA.SystemAreaID = SLI.SystemAreaID
WHERE (SA.SystemAreaID = 1)
AND W.WarehouseID = #WarehouseID
GROUP BY PSKU.ProductSKUID, PSKU.ProductSKUName, W.WarehouseName, SA.SystemAreaName, W.WarehouseID
To an effective EF statement. This is what i Have so far, my Model class and the method:
[Serializable]
public class StockReturnMethod
{
public int ProductSKUID { get; set; }
public int WarehouseID { get; set; }
public int LotID { get; set; }
public string LotName { get; set; }
public int AreaID { get; set; }
public string AreaName { get; set; }
public int BinID { get; set; }
public string BinName { get; set; }
}
public class DALStockMovement
{
scmEntitiesPrimaryCon entities = new scmEntitiesPrimaryCon();
public List<AvailibleStock> AvailibleStockQty(int warehouseID)
{
var rows = (from PLA in entities.ProductLocationAssignments
from W in entities.Warehouses
from SLI in entities.StockLineItems
from SA in entities.SystemAreas
from PSKU in entities.ProductSKUs
where W.WarehouseID == warehouseID
select new AvailibleStock() { WarehouseID = W.WarehouseID, ProductSKUID = PSKU.ProductSKUID, ProductSKUName = PSKU.ProductSKUName, WarehouseName = W.WarehouseName, Status = SA.SystemAreaName, QtyUnassigned = SLI.ProductSKUID }).ToList();
return rows;
}
Any Advice to get this to an Effective EF Statement would be appreciated
I actually used this tool called Linqer, since I had the SQL
I just popped it into that tool and it generated the Linq for me.
Here is what came out:
var SKUStock = (from sli in entities.StockLineItems
where
sli.SystemArea.SystemAreaID == 1 &&
sli.WarehouseID == warehouseID
group new { sli.ProductSKU, sli.Warehouse, sli.SystemArea, sli } by new
{
ProductSKUID = (System.Int32?)sli.ProductSKU.ProductSKUID,
sli.ProductSKU.ProductSKUName,
sli.Warehouse.WarehouseName,
sli.SystemArea.SystemAreaName,
WarehouseID = (System.Int32?)sli.Warehouse.WarehouseID
} into g
select new AvailibleStock()
{
ProductSKUID = (int)(System.Int32?)g.Key.ProductSKUID,
ProductSKUName = g.Key.ProductSKUName,
WarehouseID = (int)(System.Int32?)g.Key.WarehouseID,
WarehouseName = g.Key.WarehouseName,
Status = g.Key.SystemAreaName,
QtyUnassigned = (int)(Int64?)g.Count(p => p.sli.ProductSKUID != null)
}).ToList();
return SKUStock;
It returns exactly what i need :).

How can I add more than 2 conditions to the LlNQ where clause?

I have a LINQ query with more than 2 where conditions, but it doesn't seem to evaluate with more than 2 conditions. Is there a way to add more conditions to the where clause?
var query =
from f in XElement.Load(MapPath("flightdata3.xml")).Elements("flight")
where (string)f.Element("departurelocation") == From &&
(string)f.Element("destinationlocation") == DestCity &&
(string)f.Element("airline") == Airline
// && (string)f.Element("departuredate") == DepartDate &&
// (string)f.Element("departuretime")==DepartTime
//&& (string)f.Element("returndate")==ReturnDate &&
//(string)f.Element("returntime")==ReturnTime
orderby Convert.ToInt32(f.Element("price").Value)
select new
{
FlightNumber = (Int32)f.Element("flightnumber"),
Airline = (string)f.Element("airline"),
Departure = (string)f.Element("departureairportsymbol"),
DepartTime = (string)f.Element("departuretime"),
Destination = (string)f.Element("destinationairportsymbol"),
ArrivalTime = (string)f.Element("arrivaltime"),
Stops = (int)f.Element("numberofstops"),
Duration = (string)f.Element("duration"),
Cabin = (string)f.Element("cabin"),
Price = "$" + (Int32)f.Element("price"),
ImagePath = (string)f.Element("airlineimageurl").Value
};
LINQ absolutely allows more than two WHERE conditions. Have you tried separating the query into more manageable pieces? LINQ uses deferred execution anyway so you won't see a performance penalty in doing so.
You should also consider making a class to hold the information you're stuffing into the result.
public class FlightDetail
{
public Int32 FlightNumber { get; set; }
public String Airline { get; set; }
public String Departure { get; set; }
public String DepartureTime { get; set; }
public String Destination { get; set; }
public String ArrivalTime { get; set; }
public Int32 Stops { get; set; }
public String Duration { get; set; }
public String Cabin { get; set; }
public Int32 Price { get; set; }
public String ImagePath { get; set; }
}
Then something like this which is more readable but should also help you find whatever bug is popping up.
var flights =
from f in XElement.Load(MapPath("flightdata3.xml")).Elements("flight")
select new FlightDetail
{
FlightNumber = (Int32)f.Element("flightnumber"),
Airline = (string)f.Element("airline"),
Departure = (string)f.Element("departureairportsymbol"),
DepartTime = (string)f.Element("departuretime"),
Destination = (string)f.Element("destinationairportsymbol"),
ArrivalTime = (string)f.Element("arrivaltime"),
Stops = (int)f.Element("numberofstops"),
Duration = (string)f.Element("duration"),
Cabin = (string)f.Element("cabin"),
Price = "$" + (Int32)f.Element("price"),
ImagePath = (string)f.Element("airlineimageurl").Value
};
var flightsByLocation =
flights.
where (string)f.Element("departurelocation") == From &&
(string)f.Element("destinationlocation") == DestCity
select new FlightDetail
{
FlightNumber = (Int32)f.Element("flightnumber"),
Airline = (string)f.Element("airline"),
Departure = (string)f.Element("departureairportsymbol"),
DepartTime = (string)f.Element("departuretime"),
Destination = (string)f.Element("destinationairportsymbol"),
ArrivalTime = (string)f.Element("arrivaltime"),
Stops = (int)f.Element("numberofstops"),
Duration = (string)f.Element("duration"),
Cabin = (string)f.Element("cabin"),
Price = "$" + (Int32)f.Element("price"),
ImagePath = (string)f.Element("airlineimageurl").Value
};
There shouldn't be an issue with having more then one condition. For example, you could have something like this from an Order table.
var orderDetails = (from o in context.OrderDetails
where o.OrderID == orderID
where o.OrderName == orderName
select o).ToList();

Resources