i want to check different array position in my Linq query - c#

I am trying to count no of leaves condition year and month here is my LINQ query-
leaves = ( from x in obj.Result
where (Int64.Parse(x.status) == 1 &&
((x.start_date.Month == month &&
x.start_date.Year == selectedyear) &&
(x.end_date.Year == selectedyear || x.end_date.Month == month)))
orderby x.user_id
select x).ToList();
and I am getting a perfect result but now my start_date and end_date becomes
the array of dates, in that array multiple leaves dates are stored
now I want to check my all record within that array condition year and month
here is my model example
public DateTime start_date { get; set; }
public DateTime end_date { get; set; }
public List<string> Leaves_Date { get; set; }
and here also leaves_Date would be a string so someone has an idea that how can I set array in my LINQ query

Related

Lambda Expression LINQ Equivalent to SQL Exists Query on Same Table/Variable

I am attempting to replicate the following SQL query in LINQ using the lambda expression format (to keep it consistent with the code developed so far):
SELECT *
FROM Product p
WHERE p.DateObsolete IS NULL
OR p.DateObsolete > GETDATE()
OR EXISTS (
SELECT NULL
FROM dbo.Product p1
WHERE p1.Ref01 = p.Ref01
AND p1.Ref02 = p.Ref02
AND p1.Ref03 = p.Ref03
AND p1.Version = p.Version + 1
AND p1.DateApproved IS NULL
)
Having looked at other questions (Linq subquery same table using lambda was the closest I could find but but didn't show how to "or" conditions) on SO and elsewhere I thought the following would work but it just causes a stack overflow (seriously) exception and a message about a pdb file not being loaded, which I think is a bit of a red herring.
products = products
.Where(p => !p.DateObsolete.HasValue
|| p.DateObsolete > DateTime.Now
|| products.Any(p1 => p1.Ref01 == p.Ref01
&& p1.Ref02 == p.Ref02
&& p1.Ref03 == p.Ref03
&& p1.Version == p.Version + 1
&& p1.DateApproved == null));
products is an IQueryable variable.
Product is defined in this context as DbSet of:
public class Product
{
public int ProductID { get; set; }
[MaxLength(200)]
public string Name { get; set; }
[MaxLength(3)]
public string Ref01 { get; set; }
[MaxLength(3)]
public string Ref02 { get; set; }
public int Ref03 { get; set; }
public int Version { get; set; }
public DateTime? DateReceivedByGraphics { get; set; }
public DateTime? DateApproved { get; set; }
public DateTime? DateObsolete { get; set; }
public bool Deleted { get; set; }
public bool Discontinued { get; set; }
}
As you may gather I'm new to LINQ (and to posting questions on SO) so any help would be gratefully received.
You might be able to accomplish this with a Join.
DateTime date = DateTime.Today; // or .Now
var products = context.Products.Join(context.Products,
p1 => new { p1.Ref01, p1.Ref02, p1.Ref03 },
p2 => new { p2.Ref01, p2.Ref02, p2.Ref03 },
(p1, p2) => new { Product = p1, p1.Version, JoinedVersion = p2.Version, JoinedDateApproved = p2.DateApproved } )
.Where(x=> x.Product.DateObsolete > date && x.JoinedVersion == x.Version+1 && !x.JoinedDateApproved.HasValue)
.Select(x=>x.Product)
.ToList();
This joins Product to itself on Ref 1-3, but then selects the "left" side project, along with it's version, the "right" side's version and date approved. The Where condition isolates cases where the "right" version is 1 greater than the left and has no date approved. The result will be the "left" products that have counterparts that match those criteria.
Update:
If you have already filtered the products down to a known set of applicable products, then this will work against Objects. For example:
// given products is an IQueryable representing the filtered products...
DateTime date = DateTime.Today; // or .Now
var productList = products.ToList(); // Materialize the EF Queryable into list of entities.
productList = productList.Join(productList,
p1 => new { p1.Ref01, p1.Ref02, p1.Ref03 },
p2 => new { p2.Ref01, p2.Ref02, p2.Ref03 },
(p1, p2) => new { Product = p1, p1.Version, JoinedVersion = p2.Version, JoinedDateApproved = p2.DateApproved } )
.Where(x=> x.Product.DateObsolete > date && x.JoinedVersion == x.Version+1 && !x.JoinedDateApproved.HasValue)
.Select(x=>x.Product)
.ToList();
If your goal is to try and keep this as an IQueryable scoped to EF then I'd suspect that if it's possible, it might not be worth the complexity/time. Worst-case if you did want to preserve the IQueryable, use the above to select Product IDs into a list, then apply that list as a filter against the IQueryable.
var productList = products.ToList(); // Materialize the EF Queryable into list of entities.
// Fetch a list of applicable product IDs.
var productIds = productList.Join(productList,
p1 => new { p1.Ref01, p1.Ref02, p1.Ref03 },
p2 => new { p2.Ref01, p2.Ref02, p2.Ref03 },
(p1, p2) => new { ProductId = p1.ProductId, DateObsolete = p1.DateObsolete, p1.Version, JoinedVersion = p2.Version, JoinedDateApproved = p2.DateApproved } )
.Where(x=> x.DateObsolete > date && x.JoinedVersion == x.Version+1 && !x.JoinedDateApproved.HasValue)
.Select(x=>x.ProductId)
.ToList();
// Filter the original IQueryable.
products = products.Where(x => productIds.Contains(x.ProductId));
It was as Aleks Andreev and Ivan Stoev suggested that assigning the expression to a new variable sorted out the problem. I'm not sure why this didn't work the first time but my guess is that, after completing the query I tried to re-assign the result back to the original variable - in order not to have to change the variable name in all the code that followed my change.

Aggregate values by date

public class Results
{
public DateTime Date {get; set;}
public decimal Result {get; set;}
}
public class Sums
{
public decimal YearlySum {get; set;}
public decimal MonthlySum {get; set;}
public DateTime Date {get; set;}
}
I have a collection of Results object.
I want to populate Sums list with Yearly, Monthly sums based on date.
YearlySum is the sum of all Results' values in the provided date's year (until the provided date), and MonthlySum is the sum of Results' values in the provided date's month (until the provided date)
How to do it using Linq?
Something like this function should do the work :
public Sum GetSumFromResultsAndDate(IEnumerable<Results> results, DateTime date) {
return new Sum {
Date = date,
MonthlySum = results
.Where(r => r.Date.Year == date.Year && r.Date.Month == date.Month && r.Date <= date)
.Sum(r => r.Value) ,
YearlySum = results
.Where(r => r.Date.Year == date.Year && r.Date <= date)
.Sum(r => r.Value)
}
}
(make the sum of all Result which have the same month and year than provided date for monthly sum, and sum of all results which have the same year as the provided date for the yearly sum)
EDIT : added the "result date inferior to provided date" condition as per question clarification

LINQ query not returning unique rows

I have this LINQ query which queries SQL Express database through Entity Framework but doesn't return unique rows when written this way in Visual Studio:
var q = from s in _db.SD
where s.ID == ID && s.Time >= startDate && s.Time <= endDate
select s
It returns correct number of rows but each row has same data as first one. However, when I tested the same query in LinqPad the result returned is fine i.e. correct number of rows and unique data in each row.
Secondly, if I try to change the statment to this:
var q = from s in _db.SD
where s.ID == ID && s.Time >= startDate && s.Time <= endDate
select s.Data
Then I got correct number of rows and unique value in each row.
Can someone please help me find out the problem with code?
Thanks in advance.
EDIT:
Here's full function:
public List<SD> GetHistory(int ID, DateTime startDate, DateTime endDate)
{
var q = (from s in _db.SD
where s.ID == ID &&
s.Time >= startDate && s.Time <= endDate
select s).ToList();
return q;
}
As you are getting correct distinct data using select s.Data, then you can write a Comparer class implementing IEqualityComparer<> inside the SD class as follows:
public class SD
{
public Int16 ID { get; set; }
public Byte MT { get; set; }
public Byte Data { get; set; }
public Byte SS { get; set; }
public DateTime Time { get; set; }
public class Comparer : IEqualityComparer<SD>
{
public bool Equals(SD x, SD y)
{
return x.Data == y.Data; // look...here I m using 'Data' field for distinction
}
public int GetHashCode(SD obj)
{
unchecked // overflow is fine
{
int hash = 17;
hash = hash * 23 + obj.Data.GetHashCode();
return hash;
}
}
}
}
Then you can write the GetHistory() method passing the Comparer in the Distinct() method as follows:
public List<SD> GetHistory(int ID, DateTime startDate, DateTime endDate)
{
var list = new List<SD>();
var q = (from s in list
where s.ID == ID &&
s.Time >= startDate && s.Time <= endDate
select s).Distinct(new SD.Comparer()).ToList();
return q;
}
EDIT
The regular query uses the default (reference equality) for checking whether two objects are equal or not.
So, in order to make Distinct() work the way you want, you have to implement IEqualityComparer<T>. Otherwise, it will use the default (reference equality) for checking, which means it would only determine the elements were not distinct if they were the same exact instance. See the reference here.

Excluding rows from collection from Linq Query in MVC .net application

This is a follow up question to the one answered here: Excluding dates from Linq Query in MVC .net application - which I'm very grateful for.
I'm hoping that someone can check my syntax in my Linq query below - to confirm if it's the best way to build the query up, or if my use of the syntax is inefficient.
public class Room
{
public int RoomId { get; set; }
[Display(Name = "Room Name")]
public string Name { get; set; }
public bool Disabled { get; set; }
public virtual ICollection<Client> Clients { get; set; }
}
public class Client
{
public int ClientId { get; set; }
public int RoomId { get; set; }
public string ClientName { get; set; }
public DateTime Arrival { get; set; }
public DateTime Departure { get; set; }
public virtual Room Room { get; set; }
}
Clients lists a row for each client who has a particuar room booked. I have 3 rooms, Room 1, Room 2, and Room 3. So entries in the client table could be:
Client 1, Room 1, Mr Smith, Arr: 2012-07-08, Dep: 2012-07-10
Client 2, Room 1, Mr Jones, Arr: 2012-07-14, Dep: 2012-07-20
Client 3, Room 2, Mr Alas, Arr: 2012-07-12, Dep: 2012-07-15
Given an arrival and departure date, I'm trying to take my whole list of rooms, and take away any that have a client staying where the arrival or departure dates overlap. So using the data above, if I had an arrival date of 2012-07-12 and a departure date of 2012-07-13, then Room 2 would not be available, however, Room 1, does not have any bookings spanning that date - so Room 1 I want to leave in my result set.
So my Linq query (I'm new to Linq, so please point out where I may be going wrong) is:
var dteFrom = DateTime.Parse("2012-07-12");
var dteTo = DateTime.Parse("2012-07-13");
var rooms = (from r in Rooms
where !r.Clients.Any(
client =>
( dteFrom >= client.Arrival && dteFrom <= client.Departure )
||
( dteTo >= client.Arrival && dteFrom <= client.Departure )
||
( dteFrom <= client.Arrival && dteTo >= client.Departure )
)
select r);
Given that I'm looking to include ALL rooms, EXCEPT any that meet the criteria, can anyone confirm that my use of .Any and ! and || are correct, as far as LINQ goes?
Is there any better way within the syntax, of excluding records from the Rooms list?
Thank you again for any help,
Mark
Looks fine to me - one thing that may help readability would be to compose your query in two steps:
var prebookedRooms = rooms
.Where(room => room.Clients.Any(client =>
(dteFrom >= client.Arrival && dteFrom <= client.Departure) ||
(dteTo >= client.Arrival && dteFrom <= client.Departure) ||
(dteFrom <= client.Arrival && dteTo >= client.Departure)));
var freeRooms = rooms.Except(prebookedRooms);
Remembering that the query is only executed, when the results are enumerated - so there's no performance cost to doing this in two steps.

LINQ In Clause With Group By And TOP N Records Within Each Group

I have 2 classes as below. If I had a generic list of class Schedule like List how would I write a LINQ query such that I can get all items within this List where SourceId matches 1,2,3...X and StartTime > some date time and then I want to return top 3 elements for each SourceId (ie: group by SourceId)
Since this List would be containing huge number of records, I want to write the most efficient LINQ query
Also I want the final shape of result to be in the form of List
public class Source
{
public int SourceId { get; set; }
public string Name { get; set; }
}
public class Schedule
{
public int SourceId { get; set; }
public Source Source { get; set; }
public DateTime StartTime { get; set; }
public DateTime EndTime { get; set; }
}
after hours of googling:
var result = r.ScheduleList.
Where(s => sourceIdIntArray.Contains(s.SourceId) &&
s.StartTime >= new DateTime(2011, 09, 20)).
GroupBy(s => s.SourceId).
SelectMany(g => g.OrderBy(s => s.StartTime).
Take(3)).
ToList();
Assuming that:
no 2 sources have the same source Id
you want the top 3 elements ordered by StartTime
you're ok with having a List<KeyValuePair<int,Schedule>> as your result (top 3 grouped by Source Id and still returned as a list)
IEnumerable<Schedule> schedules;
DateTime someDateTime; // date time used for comparison
IEnumerable<int> sourceIds; // required matching source Ids
schedules.OrderBy(x=>x.StartTime)
.Where(x => x.StartTime > someDateTime)
.GroupBy(x=>x.Source.SourceId)
.Where(x=>sourceIds.Contains(x.Key))
.ToDictionary(x=>x.Key, x=>x.Take(3))
.ToList()

Categories