Linq query returns a zero - c#

I'm trying to search values according to a selected week from a datetime picker,from a datatable using following Linq queries.But It returns me only a 0.
Picking the date:
DateTime selecteddate = dateTimePicker1.Value.Date;
int currentDayOfWeek = (int)selecteddate.DayOfWeek;
DateTime sunday = selecteddate.AddDays(-currentDayOfWeek);
DateTime monday = sunday.AddDays(1);
if (currentDayOfWeek == 0)
{
monday = monday.AddDays(-7);
}
List<DateTime> dates = Enumerable.Range(0, 7)
.Select(days => monday.AddDays(days))
.ToList();
My LINQ query as follows.
var sumIncomes = dataTable
.AsEnumerable()
.Where(r => r["Type"]?.ToString() == "Income" &&
dates.Contains((DateTime)r["Date"]))
.Sum(rs => (decimal)rs["Amount"]);
var sumExpenses = dataTable
.AsEnumerable()
.Where(r => r["Type"]?.ToString() == "Expense" &&
dates.Contains((DateTime)r["Date"]))
.Sum(rs => (decimal)rs["Amount"]);
My data table looks like this.
I'm not sure what causing this behaviour.

I created table like yours and here is what I found, your LINQ expression missing some things.
List<DateTime> dates = Enumerable.Range(0, 7)
.Select(days => monday.AddDays(days))
.ToList();
dates.Add(new DateTime(2021, 5, 10)); //add for simplicity to match one record from your table didn't have time to create win form project
var sumIncomes = datatable.AsEnumerable()
.Where(r => r.Field<string>("Type").Trim() == "Income" &&
dates.Contains(r.Field<DateTime>("Date")))
.Sum(r => r.Field<int>("Amount"));
As user #user18387401 noted you should use Field extension method provided with column type in this case string. When you insert data in your table if column type is nvarchar(10) you will have data in column like this "Income " not like this "Income" so you always need to trim string value from database.
r.Field<string>("Type").Trim()
Second issue you have in Sum method (decimal)rs["Amount"] on this line program will throw runtime exception "Invalid cast" because you have int column for Amount and here you try to cast in decimal, you should provide correct type in Field method.
r.Field<int>("Amount")
Hope this helps and works for you.

Related

Filtering list if date is null or not using Linq query

I have a list of objects as shown below in which date could be null,
List<SomeObjects> myList:
myList[0] = id:1, Date:3/3/2016
myList[1] = id:2, Date:NULL
myList[2] = id:3, Date:3/3/2016
myList[3] = id:4, Date:NULL
For one condition I have to sort this list as per date as shown:
var comparingDays = DateTime.Today;
var resultsFrom = "past 7 days";
comparingDays.AddDays(-7);
myList.Where(t =>t.Date.HasValue ? Convert.ToDateTime(t.Date) >= comparingDays : ).ToList();
If Date is null I don't need those results, but I am struggling how to proceed in the false condition of HasValue here.
If "Date" is a DateTime?, you can change your query like so :
myList
.Where(t =>t.Date.HasValue && t.Date.Value >= comparingDays).ToList()
So your null dates will be filtered and then you can safely compare the date value.
A Combination of Where(filter) and OrderBy (sort) should work:
List<SomeObjects> results = myList.Where(t => t.Date.HasValue && t.Date >= comparingDays)
.OrderBy(t => t.Date).ToList();
When t.Date is a DateTime (the Convert throws doubt on that), you can simply use
// myList
// .Where(t =>t.Date.HasValue ? Convert.ToDateTime(t.Date) >= comparingDays :)
// .ToList();
myList.Where(t => t.Date >= comparingDays).ToList()
Because null is not greater (or smaller) than any Date value.

Get range of dates for movie rental

I have a movie rental application. The company would enter the movie rent date and movie rent end date. Dates can overlap (as you can have many customers). The data in the db is stored as
RecordID FromRentDate ToRentDate
1 2016-10-06 18:00:00.000 2016-10-06 20:00:00.000
2 2015-10-06 18:00:00.000 2015-10-06 20:00:00.000
3 2015-09-29 16:00:00.000 2015-09-30 17:00:00.000
4 2015-09-11 00:00:00.000 2015-09-11 00:00:00.000
5 2015-09-09 10:00:00.000 2015-09-09 14:30:00.000
When the user selects a date (using standard .Net controls) the following code is called
IEnumerable<Event> LiveDates = DataContext.Events.Where(d => d.StartDate.Value >= DateTime.Now);
IEnumerable<DateTime> AllLiveDates = null;
if (LiveDates != null && LiveDates.Count() > 0)
{
DateTime FromRentDate = LiveDates.Where(f => f.StartDate.HasValue).Min(f => f.StartDate).Value;
DateTime ToRentDate = LiveDates.Where(t => t.EndDate.HasValue).Max(f => f.EndDate).Value;
AllLiveDates = Enumerable.Range(0, int.MaxValue)
.Select(x => FromRentDate.Date.AddDays(x))
.TakeWhile(x => x <= ToRentDate.Date)
.Where(x => DataContext.Events.Any(c => x >= c.StartDate && x <= c.EndDate));
}
return AllLiveDates.ToList();
What i would like to happen is when a user selects a date, it gets all the dates from the selected date, to the end date including any inclusive dates where the movie is also out so using the above data, if i select todays date I should get all records back and the dates should be listed as:
2015-09-09
2015-09-11
2015-09-29
2015-09-30
2015-10-06 .... etc
Notice how 2015-09-29, 2015-09-30 are included but 2015-09-30 is not a start date. This is because the length of this movie rental is for 2 days (29 and 30 September).
The problem i am experiencing with the above code is that it only returns 1 date. Debugging it it seems to go into AllLiveDates code and something is removing the other dates but not sure what?
You could try something like this
var dateList = new List<DateTime>();
foreach (var ld in LiveDates)
{
for (var dt = ld.StartDate.Date; dt <= ld.EndDate.Date; dt = dt.AddDays(1))
{
dateList.Add(dt);
}
}
dateList = dateList.Distinct().ToList();
dateList = dateList.Sort((a, b) => a.CompareTo(b));
The issue appears to be that you are comparing a date value with a date and time.
Take for example the date 2015-09-09. When you compare that to the DateTime values in your table you should get zero matches, because the DateTime value 2015-09-09 00:00:00.0000 does not lie between the start and end DateTime values of any of your data points.
You will need to strip the time portions of your data points to get the comparison to work the way you want. Fortunately LINQ to SQL supports the .Date property of DateTime values, so this should work:
Try this:
AllLiveDates = Enumerable.Range(0, int.MaxValue)
.Select(x => FromRentDate.Date.AddDays(x))
.TakeWhile(x => x <= ToRentDate.Date)
.Where(x => DataContext.Events.Any(c => x >= c.StartDate.Value.Date && x <= c.EndDate.Value.Date));
Just don't look at the generated SQL... it's not pretty.

Linq sum by variable number of monthS or weekS

SqlServer table contains records of time and hours burned, materials cost by vendors
[IdPK] [DateTime] [MaterialsCost] [Hours] [Vendor_FK] [Project_FK] [Lat] [Long]
The user decides how far back he wants to see totals and for which column value, i.e. he wants totals going back - X number of monthS, or X number of weekS on any columns_FK filter value.
For e.g. he wants totals for cost, hours on either, Vendor = Nike or ProjectX, going back (1) month, (3) weeks, or 2 months from a certain date. So, I'm trying to get the totals for based on a parameterized Linq query.
Question:
Using any of columns as a filter/selector in linq, how to get the Monthly/weekly totals for cost,hours - going back (X) number of months or weeks?
Also, should I write separate queries for months vs weeks, and separate queries for each column (columns value selected by the user, is passed to me as a genric val)?
// I tried this... but just stuck
DateTime now = DateTime.Now;
Int goBack = passedinGoingBack; // amount of units to go back
var backUnits = obj.GetType().GetProperty(name); // tried getting month or week??
var getRows = table.AsEnumerable()
var columnName = passedInColumnName;
var filter = passedInValue;
.Where(r => r.Field<DateTime>(columnName).Year == now.Year
&& r.Field<DateTime>(columnName).Month == now.Month);
This question is a little bit opinion-based. You can do it in any way it is convenient for you. That's how I would do this.
First: you can use Sum LINQ function for getting total according to the given summing rule.
var monthlyRows = table.AsEnumerable()
.Where(r => r.Field<DateTime>("ColumnName").Year == now.Year
&& r.Field<DateTime>("ColumnName").Month == now.Month);
var monthlyTotalForCost = monthlyRows.Sum(r => r.Field<decimal>("CostColumn"));
var monthlyTotalForHours = monthlyRows.Sum(r => r.Field<decimal>("HoursColumn"));
var weeklyRows = table.AsEnumerable()
.Where(r => r.Field<DateTime>("ColumnName").Year == now.Year
&& r.Field<DateTime>("ColumnName").Week == now.Week);
var weeklyTotalForCost = monthlyRows.Sum(r => r.Field<decimal>("CostColumn"));
var weeklyTotalForHours = monthlyRows.Sum(r => r.Field<decimal>("HoursColumn"));
Second: I would do separate queries for weekly and monthly totals as that' a sum of different entity values and there can be a different logic.
However, I would do a helping function for me like this:
public static class TableLinqHelper
{
public static SumOfTableColumn<T>(this IEnumerable<DataRow> rows, string columnName)
{
return rows.Sum(r => r.Field<T>(columnName));
}
public static DateTime GetDate(this DataRow row)
{
return row.Field<DateTime>("DateColumn");
}
public static GetTotalForCost(this IEnumerable<DataRow> row)
{
return SumOfTableColumn<decimal>(row, "CostColumn");
}
public static GetTotalForHours(this IEnumerable<DataRow> row)
{
return SumOfTableColumn<double>(row, "HoursColumn");
}
}
var monthlyRows = table.AsEnumerable()
.Where(r => r.GetTime().Year == now.Year
&& r.GetTime().Month == now.Month);
var weeklyRows = table.AsEnumerable()
.Where(r => r.GetTime().Year == now.Year
&& r.GetTime().Week == now.Week);
var monthlyTotalForCost = monthlyRows.GetTotalForCost();
var monthlyTotalForHours = monthlyRows.GetTotalForHours();
var weeklyTotalForCost = weeklyRows.GetTotalForCost();
var weeklyTotalForHours = weeklyRows.GetTotalForHours();
Update:
Filtering: You can filter your results using Where LINQ.
Dictionary<string, object> Filters = new Dictionary<string, object>();
Filters.Add("VendorColumn", "Nike");
Filters.Add("Hours", 7.0);
foreach (var filter in Filters)
{
monthlyRows = monthlyRows.Where(r => // ...);
}
Several monthes and weeks: You can change the condition of DateTime where.
int rowsToBeShown = 4;
var monthlyRows = table.AsEnumerable()
.Where(r => r.Field<DateTime>("ColumnName") > DateTime.Now.AddMonths(-rowsToBeShown));
It will show results for the last 4 monthes.
LINQ provides very convenient and flexible tools for data manipulation. It's all up to your fantasy.

ASP.net Getting Max Date of database Date column How to avoid Null in Date Column C# My code Attached

i am trying to get last update DATE in database with in Date column my code is,
if (ds.Tables[0].Rows.Count > 0 && (ds.Tables[0].Rows[0][4].ToString() != null && ds.Tables[0].Rows[0][4].ToString() != ""))
{
try
{
DataColumn col = ds.Tables[0].Columns[4];
DataTable tbl = col.Table;
var LastUpdate = tbl.AsEnumerable()
.Max(r => r.Field<DateTime>(col.ColumnName));
date = LastUpdate.ToString();
value = date.Split(' ');
txtActivatedOn.Text = value[0];
}
catch
{
}
}
Its a part of code simply getting column 4 from data base that is DATE column and getting last date inserted in that column it is giving accurate answer but it column contains null value it result in error
How to skip null value in that column while getting date ??
Hopes for your suggestion
Thanks in Advance
Just try check for null-values in a where condition first.
Changed the Datetime to the nullable DateTime? to make the check for nullvalues work.
var LastUpdate = ds.Tables[0].AsEnumerable().Where(r => r.Field<DateTime?>(col.ColumnName) != null).Max(r => r.Field<DateTime>(col.ColumnName));
As abeldenibus said, you should filter out null values in a where condition but Field extension method is not well suited for this task.
var LastUpdate = tbl.AsEnumerable()
.Where(r => !r.IsNull(col))
.Max(r => r.Field<DateTime>(col));
date = LastUpdate.ToString();

find records by criteria and select only one if they have some equal field (LINQ)

I have a class BaseEvent and it have the following fields:
Name, RecurrenceStart, RecurrenceEnd, SimillarId
For example we have a data in a table:
Name RecurrenceStart RecurrenceEnd SimillarId
Event1 2012-02-21 09:00:00.000 2012-02-28 11:30:00.000 1
Event1 2012-02-21 04:30:00.000 2012-02-28 07:00:00.000 1
Event2 2012-02-21 06:30:00.000 2012-02-29 09:00:00.000 2
And we have the following query:
var today = DateTime.Now;
return Database.BaseEvents.Where(e => e.RecurrenceStart.Value.Date <= today
&& e.RecurrenceEnd.Value.Date >= today).ToList();
The query return all 3 records(for current date). But the first two record is the same event.
I want that in the result will only two records: 3 and (1 or 2).
How can I do this?
Thanks.
P.S. I was introduce SimillarId for this purpose, but I don't know how to build a query with it or not.
P.S.2. Change question name if it necessery.
Use .Distinct() with an IEqualityComparer<BaseEvent> as argument which you have to implement first.
Try this:
var today = DateTime.Now;
return Database.BaseEvents.Where(e => e.RecurrenceStart.Value.Date <= today
&& e.RecurrenceEnd.Value.Date >= today)
.GroupBy(v => v.Name).Select(g => g.First()).ToList();

Categories

Resources