Select where not exist in interval ? Different between four dates - c#

In the first the business is car rental system .
I want to get all car where has no orders in interval selected by user
public List<Car> SearchCar(DateTime pickdate, DateTime dropdate)
{
var db = new CarRentalDBEntities();
var temp = new List<Car>();
temp = db.Cars.Where(item =>
!item.Orders.Any
(e => e.PickUpDateTime >= pickdate && dropdate <= e.DropDataTime)
).ToList();
return temp;
}
this is the last code I write
the error is : cars still comes if order intersect with interval user choosed

As per my comment, you probably want to be checking if either the pickup date is in the range or the return date is in the range, or if the rental period is longer than the entire range:
e => (pickdate <= e.PickUpDateTime && e.PickUpDateTime < dropdate) || //picked up in period
(pickdate <= e.DropDataTime && e.DropDataTime < dropdate) || //dropped off in period
(e.PickUpDateTime < pickdate && e.DropDataTime > dropdate) //rental spans period
Notes: typo in your DropDataTime

Maybe you can use:
var temp = db.Cars.Where(item =>
item.Orders.All(e =>
e.DropDataTime <= pickdate
||
e.PickUpDateTime >= dropdate
)
).ToList();
For all existing orders e, either they finish their order in time, or else they only need the car after.
This assumes that all existing orders e on the car are "sane" in the sense that their pick-up is earlier than their drop time.

If i understand what you are trying to do,
I made the following modifications
I used All instead of !Any i find it easier to read
I put the Db Context In a using statement, good practice
I returned the output directly (no need for temp variable)
The premise as i understand it is to return all cars not and order
public List<Car> SearchCar(DateTime pickdate, DateTime dropdate)
{
using (var db = new CarRentalDBEntities())
{
return db.Cars.Where(item => item.Orders.All(e =>
// where order pickup date is less Pick or greater than drop
(e.PickUpDateTime < pickdate || e.PickUpDateTime > dropdate) &&
// where order Drop date is less Pick or greater than drop
(e.DropDataTime < pickdate || e.DropDataTime > dropdate)))
.ToList();
}
}

Related

Linq query and Foreach on large number of records from SQL database

I'm working with Entity Framework and Linq. I need to do a query on 2 properties of my object.
I have this object on the database (about 200.000 records):
public class DeviceState
{
public int ID { get; set; }
public DateTime TimeStamp { get; set; }
public string StatusCode { get; set; }
public int Device_ID { get; set; }
}
I need to do a query like this:
List<DeviceState> listState = systemDB.DeviceStates.Where(s => s.Device_ID == DeviceID).Where(l => l.TimeStamp > startDate).Where(l => l.TimeStamp < endDate).Where(s => s.StatusCode == "xx").ToList();
foreach (DeviceState status in listState)
{
// here I need to save in an object the status code and the time stamp:
object.StatusCode= status.StatusCode;
object.TimeStamp = status.TimeStamp;
}
This query takes much time (15 minutes approx). I think that is due to the creation of the list. So I tried this:
foreach (DeviceState status in systemDB.DeviceStates.Where(s => s.Device_ID == DeviceID).Where(l => l.TimeStamp > startDate).Where(l => l.TimeStamp < endDate).Where(s => s.StatusCode == "xx"))
{
// here I need to save in an object the status code and the time stamp:
object.StatusCode= status.StatusCode;
object.TimeStamp = status.TimeStamp;
}
This was much faster in creating the list. But I still have performance problems due to the foreach cycle. It takes 5ms for each element.
I need to find a solution that takes seconds to execute that.
You can do these things to help the generated query.
Return only what you need. Now you are returning everything BUT you only ever use TimeStamp so just return that. You are setting StatusCode below however you are already filtering this in the Where clause so you know that all returned items have a StatusCode of "xx" so no need to retrieve that too. This is less data coming back across the network and less cycles taken to map that data to objects and less memory that the data takes up.
You should look at the generated query by EF. You can do this using a sql profiler tool (Sql Server has one called Sql Profiler). Then look at the query plan and see if there is anything that could benifit you like adding a missing index. This profiling should be done on the database server, not in c#.
Consolidate your where clauses because it's easier to read.
Code
// list of timestamps from database
var timeStamps = systemDB.DeviceStates.Where(s => s.Device_ID == DeviceID &&
s.TimeStamp > startDate &&
s.TimeStamp < endDate &&
s.StatusCode == "xx")
.Select(x => x.TimeStamp).ToList();
If you still wanted your status code because you removed the filter you could do it like this
var timeStamps = systemDB.DeviceStates.Where(s => s.Device_ID == DeviceID &&
s.TimeStamp > startDate &&
s.TimeStamp < endDate &&
s.StatusCode == "xx")
.Select(x => new {x.TimeStamp, x.StatusCode})
.ToList();
You can do thin one where clause
List<DeviceState> listState = systemDB.DeviceStates.Where(
l => l.Device_ID == DeviceID
&& l.TimeStamp > startDate
&& l.TimeStamp < endDate
&& l.StatusCode == "xx"
).ToList();
then use for rather than foreach, because foreach slower in case of big amount data
for (int i = 0; i< listState.Count; i++ )
{
object.StatusCode= listState[i].StatusCode;
object.TimeStamp = listState[i].TimeStamp;
}
Igor's answer already shows the most obvious improvements that you should do in any case.
Now, if you handly lots of data, then you may want to do it in multiple Threads parallel with System.Threading.Tasks.Parallel.ForEach. Code (with other improvements):
var deviceStateTime = systemDB.DeviceStates.Where(s => s.Device_ID == DeviceID
&& s.TimeStamp > startDate)
&& s.TimeStamp < endDate
&& s.StatusCode == "xx");
Parallel.ForEach(deviceStateTime, (time) =>
{
object.StatusCode = "xx";
object.Timestamp = time;
});
Please note, that I did not test it (I wrote it down from memory), so I may have mistyped somewhere.

Entity Framework DateTIme Query

I'm having an issue with a linq subquery return invalid data when adding in datetime checks as part of the where clause.
This is the original query and it is returning 0; because the result set is null
var subquery =
(from item in g
from e in item.Entry
where e.Type == 1
&& e.EntryType == 2
&& item.StartDate >= priorMonthStartOfDay
&& item.EndDate <= startOfDayQueryParam
select e.Amount).Sum() ?? 0M;
I modified the query to see what the data was; here is that query and the resulting dataset.
var subquery =
(from item in g
from e in item.Entry
where e.Type == 1
&& e.EntryType == 2
select new
{
Amount = e.Amount,
SD = item.StartDate,
ED = item.EndDate,
QD = priorMonthStartOfDay
};
So then I added in the start date comparison and the results are below. The priorMonthStartOfDay is a DateTime with a value of 12/1/2015 12:00:00 AM
var subquery =
(from item in g
from e in item.Entry
where e.Type == 1
&& e.EntryType == 2
&& item.StartDate >= priorMonthStartOfDay
select new
{
Amount = e.Amount,
SD = item.StartDate,
ED = item.EndDate,
QD = priorMonthStartOfDay
};
Why is the date comparison not behaving as I would expected? Given the value of priorMonthStartOfDay, I would expect the result set to be the same for the last two queries. I'm guessing it has something to do with the time equal comparison because if I subtract a second from the priorMonthStartOfDay then the result sets match up again.
The only logical explanation could be that your priorMonthStartOfDay and/or startOfDayQueryParam variables contain time part not shown in the debugger. Note that by default milliseconds part is not shown, not to mention ticks.
To be 100% sure you are comparing against dates, change the date part of the criteria to
&& item.StartDate >= priorMonthStartOfDay.Date
&& item.EndDate <= startOfDayQueryParam.Date

Why does this LINQ statement throw a timeout error?

I am getting this error when I execute this line, through break points I detected this error.
Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding.
int? dressSerialNo;
var lstDress = (
from yy in currContext.OrderDressings
where yy.OrderID == this.OrderID
&& yy.OrderItemID == this.orderItemID
&& yy.ProductID == this.ProductID
select yy
).ToList();
if (lstDress.Count > 0)
{
dressSerialNo = (
from yy in lstDress
where yy.OrderID == this.OrderID
&& yy.OrderItemID == this.orderItemID
&& yy.ProductID == this.ProductID
select (int?)yy.SrNo
).Max();
dressSerialNo += dressSerialNo + 1;
}
else dressSerialNo = 1;
solution:- In my project I was mainlining transaction in two for some module old method with ado.net and for newly developed modules I was using the entity framework so it was creating problem in transaction. So it went aberrant.
You are using Linq-To-Entities. There is an issue with the connection to your database server. Common causes for this are:
The query is taking longer than the timeout specified in the context.
There are network related issues causing a delay.
You can optionally change the command timeout (see this question about how to do this).
You don't want to materialize your all your entities using .ToList().
You could write a single query that returns only what you're interested in:
// Get the entity that contains the max value, using Ordering
var myMaxIfAny = currContext.OrderDressings
.Where(yy => yy.OrderID == this.OrderID && yy.OrderItemID == this.orderItemID && yy.ProductID == this.ProductID)
.OrderByDescending(z => z.SrNo)
.FirstOrDefault();
if (myMaxIfAny != null)
{
// Then you got a value, retrieve the Max using myMaxIfAny.SrNo
// ...
}
else
{
// ...
}
I've formatted and commented your code:
int? dressSerialNo;
// Get all OrderDressings with matching OrderID, orderItemID and ProductID as a List<OrderDressing>
var lstDress = (from yy in currContext.OrderDressings
where yy.OrderID == this.OrderID
&& yy.OrderItemID == this.orderItemID
&& yy.ProductID == this.ProductID
select yy)
.ToList();
// If any were found,
if (lstDress.Count > 0)
{
// Execute the Where again (what else will the list contain?) and select all yy.SrNo
dressSerialNo = (from yy in lstDress
where yy.OrderID == this.OrderID
&& yy.OrderItemID == this.orderItemID
&& yy.ProductID == this.ProductID
select (int?)yy.SrNo)
.Max(); // And take the Max() of that
// Add dressSerialNo + 1 to dressSerialNo.
dressSerialNo += dressSerialNo + 1;
}
else dressSerialNo = 1;
Which seems it can be corrected and reduced to:
int? serialNumber = (from yy in currContext.OrderDressings
where yy.OrderID == this.OrderID
&& yy.OrderItemID == this.orderItemID
&& yy.ProductID == this.ProductID
select yy.SrNo)
.DefaultIfEmpty() // Might not be necessary
.Max();
if (!serialNumber.HasValue)
{
serialNumber = 1;
}
else
{
serialNumber++;
}
Please note this can cause concurrency issues if two people execute this at the same time.
The query against the database is taking too long. There are many reasons as to why this could be happening.
Try running the sql statement generated from the linq directly to the database to see if it takes as long.
Check and see if any of your columns have massive data. (like a string column filled with large volume of data)
Meanwhile try adding this to end of your connection string
Connection Timeout=30000;
I have found the issue it was creating time out problem,
in my application transaction was maintained in 2 style,
one with old ado.net style, and another with EF style,
so it created a chaos. I am to make it uniform all to change in entity-framework.

Linq-to-SQL group by date-periods depending on a parameter (daily, Monthly....)

I'm trying to make a Linq to SQL query that returns Date grouping results. The challenge is about grouping daily/weekly/monthly/quarterly depends of a enumerable parameter (periodicity). Below, my current Linq to SQL query:
var TotalGroupedInvoices = from c in entidades.InvoicesView
group c by (periodo == periodicity.Daily) ? c.InvoiceDate.Date :
period == periodicity.Weekly? c.InvoiceDate.Date.AddDays(-(double)c.InvoiceDate.DayOfWeek) :
period == periodicity.Monthly? new DateTime(c.InvoiceDate.Year,c.InvoiceDate.Month ,1) :
period == periodicity.Quarterly? new DateTime(c.InvoiceDate.Year, c.InvoiceDate.Month - (c.InvoiceDate.Month % 3) +1, 1) :
period == periodicity.Anual ? new DateTime(c.InvoiceDate.Year, 1, 1) : inicio into Periods
select new
{
period = Periods.Key,
Total = Periodos.Sum(c => c.Total)
};
For clarification, take a look at the quarterly period code fragment:
period == periodicity.Quarterly? new DateTime(c.InvoiceDate.Year, c.InvoiceDate.Month - (c.InvoiceDate.Month % 3) +1, 1)
Thus, for dates into first quarter like: January/12/2012, January/15/2012, or March/20/2012 i get all of them grouped at the quarter begginig: January/1/2012, so it's works as expected.
First I wonder about the query efficiency. What do you think about this? Maybe it would be better to translate periods in integers for SQL Server efficiency and re-translate to date periods on client, but i'm not sure about this.
On the other hand, the weekly group works grouping dates weekly into the first sunday of each week:
period == periodicity.Weekly? c.InvoiceDate.Date.AddDays(-(double)c.InvoiceDate.DayOfWeek)
...but that's incorrect for me because i'm from Spain and weeks start on Monday. How can i fix the week groups to take this into account?
So, summarizing:
What about this Linq to SQL query efficiency?
How can i group weekly by this but considering weeks from Monday to Sunday?
Thanks a lot!
PD: sorry for my English level.
To answer the first part, you don't really want to transmit the enum value into the databaes and let it make the decision of which code branch to use. You have all the information to make the decision locally.
Expression<Func<InvoicesView, DateTime>> groupingExpr = c => c.InvoiceDate.Date;
if (period == periodicity.Weekly)
{
groupingExpr = c => c.InvoiceDate.Date.AddDays(-(double)c.InvoiceDate.DayOfWeek);
}
else if (period == periodicity.Monthly)
{
groupingExpr = c => new DateTime(c.InvoiceDate.Year,c.InvoiceDate.Month ,1);
}
else if (period == periodicity.Quarterly)
{
groupingExpr = c => new DateTime(c.InvoiceDate.Year, c.InvoiceDate.Month - (c.InvoiceDate.Month % 3) +1, 1);
}
else if (period == periodicity.Anual
{
groupingExpr = c => new DateTime(c.InvoiceDate.Year, 1, 1);
}
var TotalGroupedInvoices = entidades.InvoicesView
.GroupBy(groupingExpr)
.Select(grouped => new {
period = grouped.Key,
Total = grouped.Sum(c => c.Total)
});
Here's the best I can do to blend the groupingExpr with query comprehension syntax:
var TotalGroupedInvoices = from grouped in entidades.InvoicesView.GroupBy(groupingExpr)
select new {
period = grouped.Key,
Total = grouped.Sum(c => c.Total)
};
To answer the second part, you just need to do some "day" arithmetic. You can do this inline or as a separate function (eg extension method):
public static DateTime FirstDayOfWeek (this DateTime date)
{
double offset = (date.DayOfWeek == DayOfWeek.Sunday) ? -6 : (DayOfWeek.Monday - date.DayOfWeek);
return date.AddDays(offset);
}
Then in your LINQ:
period == periodicity.Weekly ? c.InvoiceDate.FirstDayOfWeek
However, given that you are retrieving this from SQL you may just want to look at the date functions there eg DATEPART and return them in your queries

How do I find the intersect of two sets of non-contiguous Times?

I am trying to build a tool that calculates something called quota based upon when employees are scheduled to work and when they request off.
My ShiftSet object is a set of Shift objects which consist of a StartTime and EndTime (both of type time(7). Each ShiftSet corresponds to a day.
ScheduleExceptions are times that an employee has off. There can be any number of overlapping or non-overlapping ScheduleExceptions in a day. They are of the datetime data type.
An example of a ShiftSet:
08:00-10:00
10:00-12:00
13:00-15:00
15:00-17:00
An example of ScheduleExceptions for that same day:
07:30-10:30
14:35-16:00
What I need to do is to find the amount of time that the employee is working on a day. The way I can figure to do this is to calculate the intersection of ShiftSet and the inverse of ScheduleExceptions.
How would I do this with time? I would prefer to use Linq if possible.
Check out this great article at CodeProject
It's probably way too broad for your specific problem but it will probably give you a good starting point on how to solve it.
As InBetween mentioned, there are libraries out there that have solved this problem, but they solve many related problems as well. If you are wanting to just tackle this particular problem without taking on another dependency, you can try the following.
// Finds ones with absolutely no overlap
var unmodified = shifts.Where(s => !exceptions.Any(e => s.Start < e.End && s.End > e.Start));
// Finds ones entirely overlapped
var overlapped = shifts.Where(s => exceptions.Any(e => e.End >= s.End && e.Start <= s.Start));
// Adjusted shifts
var adjusted = shifts.Where(s => !unmodified.Contains(s) && !overlapped.Contains(s))
.Select(s => new Shift
{
Start = exceptions.Where(e => e.Start <= s.Start && e.End > s.Start).Any() ? exceptions.Where(e => e.Start <= s.Start && e.End > s.Start).First().End : s.Start,
End = exceptions.Where(e => e.Start < s.End && e.End >= s.End).Any() ? exceptions.Where(e => e.Start < s.End && e.End >= s.End).First().Start : s.End
});
var newShiftSet = unmodified.Union(overlapped).Union(adjusted);
It's a basic example, though it could be compacted (albeit less-readable) and improved.
I didn't test bellow code, may be there is some bugs, Also I wrote it in textpad may be there are invalid characters, Idea is simple, and I try to use meaningful variables.
var orderedShifts = ShiftSets.OrderBy(x=>x.StartDate).ToList();
var compactShifts = new List<Shift>();
compactShifts.Add(orderedShifs[0]);
foreach (var item in orderedShift)
{
if (item.Start <= compactShifts[compactShifts.Count-1].End
&& item.End > compactShifts[compactShifts.Count-1].End)
{
compactShifts[compactShifts.Count-1].End = item.End;
}
else if (item.Start > compactShifts[compactShifts.Count-1].End)
compactShifts.Add(item);
}
//run similar procedure for schedule exceptions to create compact schedules.
var validShifts = new List<Shift>();
foreach (var item in compactShifts)
{
var shiftCheatingPart = compactExceptions
.FirstOrDefault(x=>x.Start < item.Start
&& x.End > item.End)
if (shiftCheatingPart != null)
{
if (item.End <= shiftCheatingPart.End)
continue;
validShifts.Add(new Shift{Start = shiftCheatingPart.End,End = item.End);
}
}
var totalTimes = validShifts.Sum(x=>x.End.Sunbtract(x.Start).TotalHours);
A very crude solution would be something like
void Main()
{
var workTime = new List<ShiftSet> {
new ShiftSet{StartTime= new TimeSpan(8,0,0),EndTime= new TimeSpan(10,0,0)},
new ShiftSet{StartTime= new TimeSpan(10,0,0),EndTime= new TimeSpan(12,0,0)},
new ShiftSet{StartTime= new TimeSpan(13,0,0),EndTime= new TimeSpan(15,0,0)},
new ShiftSet{StartTime= new TimeSpan(15,0,0),EndTime= new TimeSpan(17,0,0)}
};
var missingTime= new List<ShiftSet> {
new ShiftSet{StartTime= new TimeSpan(7,30,0),EndTime= new TimeSpan(10,30,0)},
new ShiftSet{StartTime= new TimeSpan(14,35,0),EndTime= new TimeSpan(16,0,0)}
};
Console.WriteLine(workTime.Sum(p=>p.Shift()) - missingTime.Sum(p=>p.Shift()));
}
public class ShiftSet
{
public TimeSpan StartTime {get;set;}
public TimeSpan EndTime {get;set;}
public double Shift() {return (EndTime-StartTime).TotalMinutes;}
}
I calculate a worktime in minutes so I can sum more easily using linq
I am also missing specific shift information which I think don't belong with ShiftSet class
Because the employee is not scheduled to work from 7:30 to 8:00, we would not include that time

Categories