Linq sql - where should I put phrase with "where" - linq

This is my query, an it almost works. I need to add to this query " where(k.IdUser == d.UserDoc.
The problem appears when I add this. Wherever I put this it stops working. Join isn't option here.
var documents = (from d in DocumentDAO.GetDocument()
from k in UserDAO.GetUsers()
where (DateTime.Now <= d.ExpirationDate)
select new DocumentUI
{
Title = d.Title,
Description = d.Description,
DateOfAdd = d.DateOfAdd,
ExpirationDate = d.ExpirationDate,
UserDoc = d.UserDoc,
User = new UserUI {
FirstName = k.FirstName,
LastName = k.LastName}
}).ToList();
My app gets the data from database and show this data in window. If I start my app with this query ( I have only one documents in db) it shows me the same document but with all Users, and I want to my application shows me only this user who Id is as foreign key in documents (UserDoc).
I tried somethig like this:
var documents = (from d in DocumentDAO.GetDocument()
from k in UserDAO.GetUsers()
where ((DateTime.Now <= d.ExpirationDate) && (d.UserDoc == k.IdUser))
select new DocumentUI
{
Title = d.Title,
Description = d.Description,
DateOfAdd = d.DateOfAdd,
ExpirationDate = d.ExpirationDate,
UserDoc = d.UserDoc,
User = new UserUI {
FirstName = k.FirstName,
LastName = k.LastName}
}).ToList();
And:
var documents = (from d in DocumentDAO.GetDocument()
from k in UserDAO.GetUsers().Where(k => k.IdUser == d.UserDoc)
where (DateTime.Now <= d.ExpirationDate)
select new DocumentUI
{
Title = d.Title,
Description = d.Description,
DateOfAdd = d.DateOfAdd,
ExpirationDate = d.ExpirationDate,
UserDoc = d.UserDoc,
User = new UserUI {
FirstName = k.FirstName,
LastName = k.LastName}
}).ToList();
and in both case my app shows me nothing
this it class DocumentUI:
public class DocumentUI
{
public string Title { get; set; }
public string Description { get; set; }
public DateTime DateOfAdd { get; set; }
public DateTime ExpirationDate { get; set; }
public int UserDoc { get; set; }
public UserUI User { get; set; }
}

The below query (Same as your first Query) should return List<DocumentUI> provided you have proper data which satisfy ((DateTime.Now <= d.ExpirationDate) && (d.UserDoc == k.IdUser))
var documents = (from d in DocumentDAO.GetDocument()
join k in UserDAO.GetUsers()
on d.UserDoc equals k.IdUser
where (d.ExpirationDate >= DateTime.Now)
select new DocumentUI
{
Title = d.Title,
Description = d.Description,
DateOfAdd = d.DateOfAdd,
ExpirationDate = d.ExpirationDate,
UserDoc = d.UserDoc,
User = new UserUI {
FirstName = k.FirstName,
LastName = k.LastName}
}).ToList();
Still, If you are not getting result verify the values in Var documents = DocumentDAO.GetDocument(), var users = UserDAO.GetUsers().

Related

How do I use LINQ group by clause to return unique employee rows?

I'm pretty new to LINQ, and I can't for the life of me figure this out. I've seen lots of posts on how to use the group by in LINQ, but for some reason, I can't get it to work. This is so easy in ADO.NET, but I'm trying to use LINQ. Here's what I have that is relevant to the problem. I have marked the part that doesn't work.
public class JoinResult
{
public int LocationID;
public int EmployeeID;
public string LastName;
public string FirstName;
public string Position;
public bool Active;
}
private IQueryable<JoinResult> JoinResultIQueryable;
public IList<JoinResult> JoinResultIList;
JoinResultIQueryable = (
from e in IDDSContext.Employee
join p in IDDSContext.Position on e.PositionID equals p.PositionID
join el in IDDSContext.EmployeeLocation on e.EmployeeID equals el.EmployeeID
where e.PositionID != 1 // Do not display the super administrator's data.
orderby e.LastName, e.FirstName
// ***** Edit: I forgot to add this line of code, which applies a filter
// ***** to the IQueryable. It is this filter (or others like it that I
// ***** have omitted) that causes the query to return multiple rows.
// ***** The EmployeeLocationsList contains multiple LocationIDs, hence
// ***** the duplicates employees that I need to get rid of.
JoinResultIQueryable = JoinResultIQueryable
.Where(e => EmployeeLocationsList.Contains(e.LocationID);
// *****
// ***** The following line of code is what I want to do, but it doesn't work.
// ***** I just want the above join to bring back unique employees with all the data.
// ***** Select Distinct is way too cumbersome, so I'm using group by.
group el by e.EmployeeID
select new JoinResult
{
LocationID = el.LocationID,
EmployeeID = e.EmployeeID,
LastName = e.LastName,
FirstName = e.FirstName,
Position = p.Position1,
Active = e.Active
})
.AsNoTracking();
JoinResultIList = await JoinResultIQueryable
.ToListAsync();
How do I get from the IQueryable to the IList only returning the unique employee rows?
***** Edit:
Here is my current output:
[4][4][Anderson (OH)][Amanda][Dentist][True]
[5][4][Anderson (OH)][Amanda][Dentist][True]
[4][25][Stevens (OH)][Sally][Dental Assistant][True]
[4][30][Becon (OH)][Brenda][Administrative Assistant][False]
[5][30][Becon (OH)][Brenda][Administrative Assistant][False]
Actually you do not need grouping here, but Distinct. Ordering before Distinct or grouping is useless. Also AsNoTracking with custom projection is not needed.
var query =
from e in IDDSContext.Employee
join p in IDDSContext.Position on e.PositionID equals p.PositionID
join el in IDDSContext.EmployeeLocation on e.EmployeeID equals el.EmployeeID
where e.PositionID != 1 // Do not display the super administrator's data.
select new JoinResult
{
LocationID = el.LocationID,
EmployeeID = e.EmployeeID,
LastName = e.LastName,
FirstName = e.FirstName,
Position = p.Position1,
Active = e.Active
};
query = query.Distinct().OrderBy(e => e.LastName).ThenBy(e => e.FirstName);
JoinResultIList = await query.ToListAsync();
The problem is that few employees have more than one location is causing the results to be repeated.You can handle it in multiple ways. Im using Let clause to tackle the issue in the below example
public class Employee
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int EmployeeID { get; set; }
public int PositionID { get; set; }
}
public class EmployeeLocation
{
public int EmployeeID { get; set; }
public int LocationID { get; set; }
}
public class Position
{
public int PositionID { get; set; }
public string Position1 { get; set; }
}
public class Location
{
public int LocationID { get; set; }
}
public class JoinResult
{
//Suggestion : Insetad of LocationID there should be a varibale that has all the locations of an employee
public IEnumerable<int> LocationIDs;
public int LocationID;
public int EmployeeID;
public string LastName;
public string FirstName;
public string Position;
public bool Active;
}
//Setting up mock data
List<Position> positions = new List<Position>();
positions.Add(new Position() { Position1 = "Dentist", PositionID = 2 });
positions.Add(new Position() { Position1 = "Dental Assistant", PositionID = 3 });
positions.Add(new Position() { Position1 = "Administrative Assistant", PositionID = 4 });
List<Employee> employees = new List<Employee>();
employees.Add(new Employee() { EmployeeID = 4, FirstName = "Amanda", LastName = "Anderson (OH)", PositionID = 2 });
employees.Add(new Employee() { EmployeeID = 25, FirstName = "Sally", LastName = "Stevens (OH)", PositionID = 3 });
employees.Add(new Employee() { EmployeeID = 30, FirstName = "Brenda", LastName = "Becon (OH)", PositionID = 4 });
List<Location> locations = new List<Location>();
locations.Add(new Location() { LocationID = 4 });
locations.Add(new Location() { LocationID = 5 });
List<EmployeeLocation> employeeLocation = new List<EmployeeLocation>();
employeeLocation.Add(new EmployeeLocation() { LocationID = 4, EmployeeID = 4 });
employeeLocation.Add(new EmployeeLocation() { LocationID = 5, EmployeeID = 4 });
employeeLocation.Add(new EmployeeLocation() { LocationID = 4, EmployeeID = 25 });
employeeLocation.Add(new EmployeeLocation() { LocationID = 4, EmployeeID = 30 });
employeeLocation.Add(new EmployeeLocation() { LocationID = 5, EmployeeID = 30 });
var result = (from e in employees
join p in positions on e.PositionID equals p.PositionID
let employeeLocations = (from el in employeeLocation where el.EmployeeID == e.EmployeeID select new { LocationID = el.LocationID })
where e.PositionID != 1 // Do not display the super administrator's data.
orderby e.LastName, e.FirstName
select new JoinResult
{
LocationID = employeeLocations.Select(p=>p.LocationID).First()//Here its just selecting the first location,
LocationIDs = employeeLocations.Select(p=> p.LocationID),//This is my suggestion
EmployeeID = e.EmployeeID,
LastName = e.LastName,
FirstName = e.FirstName,
Position = p.Position1,
}).ToList();
Okay. So here is the solution I came up with. I installed the morelinq NuGet package, which contains a DistinctBy() method. Then I added that method to the last line of the code shown in my problem.
JoinResultIList = JoinResultIQueryable
.DistinctBy(jr => jr.EmployeeID)
.ToList();

Extending a Select.... to accommodate more fields as extension using EF

I have a class:
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public int Salary { get; set; }
public string Address {get;set;}
}
And query using Entity Framework is:
var selectedEmployee = entities.Employees
.Where(e=>e.Salary>10000)
.Select(emp => new EmpDTO
{
Id = emp.Id,
Name = emp.Name,
Salary = emp.Salary
});
My question is:
I want to allow extending this query without rewriting the base query. It should allow adding a new field in the .Select(.....) by extending the above query.
Without rewriting the complete query:
var selectedEmployee = entities.Employees
.Where(e=>e.Salary>10000)
.Select(emp => new EmpDTO
{
Id = emp.Id,
Name = emp.Name,
Salary = emp.Salary,
Address = emp.Address
});
How can I do that?
Thanks
If I understand, you can try this:
public IQuerable<EmpDTO> GetEmployee(Func<Employee, EmpDTO> projection = null)
{
if(projection == null)
projection = emp => new EmpDTO {
Id = emp.Id,
Name = emp.Name,
Salary = emp.Salary,
};
return entities.Employees.Where(e => e.Salary > 10000).Select(projection);
}
Implementation:
var query = classInstance.GetEmployee();
//or
query = classInstance.GetEmployee(emp => new EmpDTO {
Id = emp.Id,
Name = emp.Name,
Salary = emp.Salary,
Address = emp.Address
});
If you always want to get some set of fields, like Id, Name and
Salary and sometimes take additional fields(and specify only their
as method arguments), you should to take all fields from DB and only
then filter them depends on your condition - it is bad practice to do
SELECT *, so you should get default set of fields or specify all desired fields mannualy.
Solution with SELECT *:
public List<EmpDTO> GetEmployee(Func<Employee, EmpDTO> projection)
{
var query = entities.Employees.Where(e => e.Salary > 10000).ToList().Select(x => {
var item = projection == null ? new EmpDTO() : projection(x);
item.Id = x.Id;
item.Name = x.Name;
item.Salary = x.Salary;
return item;
}).ToList();
}
At this case return value is List<T> not IQuerable<T>;
Implementation:
var items = classInstance.GetEmployee(emp => new EmpDTO { Address = emp.Address });
//items also will contain fields: Id, Name and Salary by default

left join two tables in same DataContext LinQ in c#

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.

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