How to handle date range query in c# - c#

I have a params which contains a start date and end date and then use it to query, but I wanted to handle that even if there is no start and enddate, it will will query data. How do we handle that in C#?
So that if there is no startDate and endDate, then it will just proceed on the query.
The filteredData variable is the query. The issue right now is that when there is no startDate and endDate it will not query the data, so the solution if to handle date range if it has no value. Any idea guys? Thanks.
#code snippet
public async Task<IPagedList<TransactionListDto>> GetList(DateTime? startDate , DateTime? endDatestring status, int id, string sortKey, string sortOrder, string searchString, int page, int pageSize, string transactionType, string repmFilter, string transactionSubType, string masterBrokerCompany, string masterBrokerName)
{
var sortKeys = JsonConvert.DeserializeObject<List<string>>(sortKey);
var sortOrders = JsonConvert.DeserializeObject<List<string>>(sortOrder);
List<string> statusValues = new List<string>();
List<string> transactionTypeValues = new List<string>();
if (!string.IsNullOrEmpty(status))
{
statusValues = status.Split(',').ToList();
}
if (!string.IsNullOrEmpty(transactionType))
{
transactionTypeValues = transactionType.Split(',').ToList();
}
.......
var filteredData = mappedData.Where(x => (masterBrokerCompanyValues.Count == 0 || masterBrokerCompanyValues.Contains(x.MasterBrokerCompany)) && x.TargetCompletionDate >= startDate && endDate <= x.TargetCompletionDate);
var paginatedData = await AppUtil.MultipleSort<TransactionListDto>(
filteredData.AsQueryable(), sortKeys, sortOrders).ToPagedListAsync(page, pageSize);

The short answer for what you want is to make those optional parameters:
public async Task<IPagedList<TransactionListDto>> GetList(string status, int id, string sortKey, string sortOrder, string searchString, int page, int pageSize, string transactionType, string repmFilter, string transactionSubType, string masterBrokerCompany, string masterBrokerName, DateTime? startDate = null, DateTime? endDate = null)
Note: they have to be moved to the end.
The better answer is to consolidate all your parameters into a Filter class, as it appears you are using them as filters. This will allow more of them to become optional as well.
public class Filter {
public string status { get; set; }
public int id { get; set; }
public string sortKey { get; set; }
public string sortOrder { get; set; }
public string searchString { get; set; }
public int page { get; set; }
public int pageSize { get; set; }
public string transactionType { get; set; }
public string repmFilter { get; set; }
public string transactionSubType { get; set; }
public string masterBrokerCompany { get; set; }
public string masterBrokerName { get; set; }
public DateTime? startDate { get; set; }
public DateTime? endDate { get; set; }
}
public async Task<IPagedList<TransactionListDto>> GetList(Filter filter) {
...
}
The great thing about the Filter class is that if it's coming from your action method in MVC for example, your signature can change in the exact same way and it will just work.

Just replace your potential null values by something that won't be limiting :
Datetime startDateForFilter = startDate ?? Datetime.Min;
Datetime endDateForFilter = endDate ?? Datetime.Max;

If I understand your problem correctly, if startDate and endDate are not given, your query returns an empty list.
It's probably because when you create instance of Datetime, it is automatically initialized to default value. That means, that both values are same. Try to find lowest possible date and highest possible date and set them when initialized.
Datetime startDate = DateTime.MinValue;
Datetime endDate= DateTime.MaxValue;

Related

How to query Entity Framework database for records between 2 dates, then return that information for display on screen

I have an Entity MVC app with a code-first database. I need to produce a search box to search between 2 dates and return the records between those dates.
I will call the method with jQuery/ajax and render the results in a table.
I've tried writing an API, with no success. I am not even sure if this is the correct way to go about it?
namespace Areometrex_Leaflet.Models
{
[Table ("Flight_Lines")]
public class Flight_line
{
[Key]
public string Swath_name { get; set; }
public string Flight_name { get; set; }
public string Swath_record { get; set; }
public string Flight_date { get; set; }
public decimal Start_lat { get; set; }
public decimal Start_long { get; set; }
public decimal End_lat { get; set; }
public decimal End_long { get; set; }
public decimal Altitude { get; set; }
public DateTime Time_start { get; set; }
public DateTime Time_end { get; set; }
public string Sensor { get; set; }
}
public class FlightLineContext : DbContext
{
public DbSet<Flight_line> Flight_Lines { get; set; }
}
}
This is my model that holds the objects in the database. I need to search the "Flight_date" property, that is held in my DB in this following format as an "nvarchar" :
17/11/2018 11:09:18 PM
My current API looks something like this:
[HttpPost]
public IEnumerable<Flight_line> SearchFlight_Line()
{
string start, end;
var rc = RequestContext;
var data = rc.Url.Request.GetQueryNameValuePairs();
{
start = data.FirstOrDefault().Value ?? string.Empty;
end = data.LastOrDefault().Value ?? string.Empty;
}
//db format: 17/11/2018 11:22:56 PM
var s = DateTime.Parse(start);
var flightSearch = new List<Flight_line>();
using (_context)
{
var sql = $"SELECT * FROM Flight_Lines WHERE Flight_Date BETWEEN '{start}' AND '{end}'";
flightSearch = _context.Flight_Lines.SqlQuery(sql).ToList<Flight_line>();
}
return flightSearch;
}
Ideally, I want to call this API with jquery/Ajax and return results to be displayed in an MVC view. My guess is that this is dead easy, but I am only learning and I'm running out of ideas. I would have thought this was really simple, but I am struggling to find the answers I am looking for online, which leads me to believe perhaps I am doing it wrong?
First of all, don't save dates as string in your database, you will just have problems later on.
Instead of:
public string Flight_date { get; set; }
Set it up as DateTime:
public DateTime Flight_date { get; set; }
As far as the query for searching flights go, you can try this. This will return a list of "Flight_line" objects which you can then return wherever you need.
DateTime start = DateTime.Now;
DateTime end = DateTime.Now.AddDays(7);
var flights = _context.Flight_line.Where(f => f.Flight_date >= start && f.Flight_date <= end).ToList();

Pass property as parameter to create a filtering method with LINQ/C#?

Suppose I have these classes:
private class Model
{
public DateTime FirstDate { get; set; }
public DateTime SecondDate { get; set; }
public DateTime FinalDate { get; set; }
public string Name { get; set; }
}
private class Mommasan : Bandana
{
public DateTime RestDate { get; set; }
public DateTime TurboDate { get; set; }
public string Name { get; set; }
}
All dates need to be filtered up to 3 days. So how can I create a generic filtering method for the DateTime properties? Using IQueryable? I was hoping it would end up something like this:
(Obviously will not compile, but I'm guessing the idea)
void DoSpecificFilter<T>(ref IQueryable<T> query, DateTimeProperty property)
{
DateTime today = today;
query = query.Where(a => property <= today && today <= property.AddDays(3);
}
So if I need to filter Models who are on their final date, something like:
DoSpecificFilter<Model>(ref alreadyFilteredQuery, a => a.FinalDate);
and Mommasans who are on rest:
DoSpecificFilter<Mommasan>(ref alreadyFilteredQuery, a => a.RestDate);
Is this kind of thing possible at all or am I having the entirely wrong idea? Thanks!
You could work with DateTime-Selectors. For multiple DateTime fields you would have to chain them together. It would probably be easier to write an extention method for this:
public static class ExtentionMethods
{
public static IQueryable<T> DoSpecificFilter<T>(
this IQueryable<T> query,
Expression<Func<T, DateTime>> dateSelector,
DateTime filterValue,
bool blnTopLimit)
{
return query.Where(a => (blnTopLimit && dateSelector.Compile()(a) < filterValue)
|| (!blnTopLimit && dateSelector.Compile()(a) > filterValue));
}
}
Then you could use it like this:
var query = queryableCollection
.DoSpecificFilter((a) => a.RestDate, DateTime.Today, false)
.DoSpecificFilter((a) => a.TurboDate, DateTime.Today, true);
Why not write an extension method on DateTime, and not look at the class at all? Something like ...
public static class Extensions
{
public static IQueryable<T> DoSpecificFilter<T>(this DateTime value, IQueryable<T> query)
{
DateTime today = DateTime.Now;
IQueryable<T> ret = query.Where(a => value <= today && today <= value.AddDays(3));
return ret;
}
}

Sorting a ListBox in c# with multiple fields

Is there a way to sort a list box on one field then another?
(I saw the other posts but I think this is a bit more involved). I could do this the long way but thought there was a faster shorthand version I just wasn't aware of.
Basically this reads a directory for all the folders in it with the format of:
DATENAME
I parse out the name from the date. I need to organize these by name THEN by date (the second filter is what is tripping me up).
So a folder of:
12012016TULLY
1202019LAVA
2202018LAVA
5162019CLOUD
5202020LAVA
would look like
5162019CLOUD
2202018LAVA
1202019LAVA
5202020LAVA
12012016TULLY
So this is what I have:
class MyListBoxItem
{
public string StudyBaseFolder { get; set; }
public string StudyName { get; set; }
public string UserLastName { get; set; }
public string StudyDate { get; set; }
}
List<MyListBoxItem> studiesAndFolders = new List<MyListBoxItem>();
//later int he code i build a list of studyNames (which is a path and I pasre the path here too)
foreach (string sn in studyName)
{
//get user name
String lastName = getLastName(sn);
String theDate = getDate(sn);
//can I organize this based on the LAST NAME THEN THE DATE
studiesAndFolders.Add(new MyListBoxItem { StudyBaseFolder = path, StudyName = sn, UserLastName = lastName, StudyDate = theDate });
}
Then I finally add this to the listbox.
listDirectories.Items.Clear();
//I do it this way so a double click on an item gets the object back and I can do things with it.
foreach(MyListBoxItem direc in studiesAndFolders)
{
listDirectories.Items.Add(direc);
}
listbox.sorted=true didn't help, and I am sure there might be an expression (LINQ to the rescue?). I was just going to do it the long way with a ton of cases when I take the studiesAndFolders and put it to the list.
This code sorts by name, then by date. And should be easy to read.
foreach(MyListBoxItem direc in studiesAndFolders.OrderBy(x => x.StudyName).ThenBy(x => x.StudyDate))
{
listDirectories.Items.Add(direc);
}
As others have noted, you should be storing the StudyDate as a DateTime unless you want it sorted alphabetically.
Firstly, change type of StudyDate to DateTime
class MyListBoxItem
{
...
public DateTime StudyDate { get; set; }
}
After, create new comparer
public class MyListBoxItemComparer : IComparer<MyListBoxItem>
{
public int Compare(MyListBoxItem x, MyListBoxItem y)
{
if (x.StudyName == y.StudyName)
{
return x.StudyDate.CompareTo(y.StudyDate);
}
return String.Compare(x.StudyName, y.StudyName, StringComparison.Ordinal);
}
}
Finally, use SortedSet instead of List
SortedSet<MyListBoxItem> studiesAndFolders = new SortedSet<MyListBoxItem>(new MyListBoxItemComparer());
For ordering based on the date correctly you need StudyDate to be of type DateTime
class MyListBoxItem
{
public string StudyBaseFolder { get; set; }
public string StudyName { get; set; }
public string UserLastName { get; set; }
public DateTime StudyDate { get; set; }
}
Then you can order by using LINQ extension methods.
var orderedDirectories =
directories.OrderBy(dir => dir.StudyName)
.ThenBy(dir => dir.StudyDate);
foreach (var directory in orderedDirectories)
{
listBox.Items.Add(directory);
}
You can override .ToString() method in MyListBoxItem class that listbox will display items as you want.
class MyListBoxItem
{
public string StudyBaseFolder { get; set; }
public string StudyName { get; set; }
public string UserLastName { get; set; }
public DateTime StudyDate { get; set; }
public override string ToString()
{
return $"{StudyDate:MMddyyyy}{StudyName}";
}
}
If you can use LINQ in your project, first you should make StudyDate type of DateTime. Then you could do this:
MyList.OrderBy(x => x.StudyName).ThenByDescending(x=>x.StudyDate).ToList()

Date format change upon update event

I'm facing an issue with date formatting. Upon calling up the UpdateItem action, the date format for CreatedAt gets messed up. I'm using JSON by the way, so must be something to do with date serialization.
Model:
public class Item
{
public int ItemId { get; set; }
public string ItemName { get; set; }
public string CreatedBy { get; set; }
public DateTime? CreatedAt { get; set; }
public string UpdatedBy { get; set; }
public DateTime? UpdatedAt { get; set; }
}
Create action:
public int CreateItem(Item item)
{
var item = new Item();
viewModel.CopyToItem(item);
item.CreatedBy = WebSecurity.CurrentUserName;
item.CreatedAt = DateTime.Now;
db.Items.Add(item);
db.SaveChanges();
return item.ItemId;
}
Update action:
public void UpdateItem(Item item)
{
item.UpdatedBy = WebSecurity.CurrentUserName;
item.UpdatedAt = DateTime.Now;
db.SaveChanges();
}
The incorrect date format:
/Date(1395366469723)/
It should be:
2014-03-21T09:50:01.747
I tried this in the controller but get a String was not recognized as a valid DateTime' error.
string isoJson = JsonConvert.SerializeObject(DateTime.Now, new IsoDateTimeConverter());
item.CreatedAt = DateTime.ParseExact(isoJson, "yyyy-MM-dd hh:mm:ss.ttt", CultureInfo.InvariantCulture);
Using non-nullable DateTime in the model didn't fix it either.
Javascript uses Unix Time. If you are wanting to get a DateTime object with the given javascript date value, create a new DateTime object from 1/1/1970 and then add the milliseconds.
Observe:
var dt = new DateTime(1970, 1, 1).AddMilliseconds(1395366469723);
// "21/03/2014 1:47:49 AM"

Setting variable types from CSV

var csv =
from line in File.ReadAllLines("C:/file.csv")
let customerRecord = line.Split(',')
select new Customer()
{
contactID = customerRecord[0],
surveyDate = customerRecord[1],
project = customerRecord[2],
projectCode = customerRecord[3]
};
public class Customer
{
public string contactID { get; set; }
public string surveyDate { get; set; }
public string project { get; set; }
public string projectCode { get; set; }
}
I'd like to be able to read the surveyDate as a DateTime in order to compare it with other DateTime fields I'm joining to the csv file.
I've tried just setting surveyDate as Date.Time in the class, and I've tried converting it in the select statement, but both fail with the following error:
FormatException: The string was not recognized as a valid DateTime. There is an unknown word starting at index 0.
I do have to work with the csv file, so I can't add the data to sql server.
contactID,responseDate,project,projectID
1,6/4/2009,0-3 months,1
2,6/11/2009,0-3 months,1
You need to parse it when reading as well as changing the type. I'm not sure how you tried it in the first place. Also, use DateTime.ParseExact if you know the format.
var csv =
from line in File.ReadAllLines("C:/file.csv")
let customerRecord = line.Split(',')
select new Customer()
{
contactID = customerRecord[0],
surveyDate = DateTime.Parse(customerRecord[1]),
project = customerRecord[2],
projectCode = customerRecord[3]
};
public class Customer
{
public string contactID { get; set; }
public DateTime surveyDate { get; set; }
public string project { get; set; }
public string projectCode { get; set; }
}
Judging by the error you just posted, I think it's probably double-quoted. Try trimming the quotes:
DateTime.Parse(customerRecord[1].Trim('"'))
Right... Because you can never know what the input will be from the CSV you can't always try and implicitly convert the string to a DateTime and because you want to do the convert of a string within the select a way to do this is an extension method.
public static class StringExtensions
{
public static DateTime TryParseDate(this string strToParse)
{
DateTime dt = new DateTime();
DateTime.TryParse(strToParse, out dt);
return dt;
}
}
Then when you want to convert in the select do it like this:
var csv = from line in File.ReadAllLines("C:/file.csv")
let customerRecord = line.Split(',')
select new Customer()
{
contactID = customerRecord[0],
surveyDate = customerRecord[1].TryParseDate(),
project = customerRecord[2],
projectCode = customerRecord[3]
};
You have your own method. Hope this helps and works for you.
The string you want to parse will be passed to the method and then you can handle it and return a valid date time, handling incorrect inputs.
http://msdn.microsoft.com/en-us/library/bb383977.aspx
See this link for an explanation
After changing Customer so that SurveyDate is a DateTime then:
select new Customer()
{
contactID = customerRecord[0],
surveyDate = DateTime.Parse(customerRecord[1]),
project = customerRecord[2],
projectCode = customerRecord[3]
};
You may need to use forms of Parse or ParseExact that take a format string, to be sure of the correct result. That format string depends on the format used in the file.
Alternatively, you could create a Parse static method on Customer:
public class Customer
{
public string contactID { get; set; }
public DateTime surveyDate { get; set; }
public string project { get; set; }
public string projectCode { get; set; }
public static Customer Parse(string line)
{
var parts = line.Split(',');
return new Customer{
contactID = parts[0],
surveyDate = DateTime.Parse(customerRecord[1]),
project = customerRecord[2],
projectCode = customerRecord[3]
};
};
Then:
var csv =
from line in File.ReadLines("C:/file.csv")
select Customer.Parse(line);
This has an advantage if obtaining a Customer from such a formatted string happens more than once in your code. It needlessly fills Customer with cruft if this is the only place you do it. Note I favour ReadLines over ReadAllLines to get a line at a time rather than read the whole thing before starting do produce customers.
Incidentally, .NET conventions favour capitals on property names. SurveyDate rather than surveyDate etc.
I've tried just setting surveyDate as Date.Time in the class, and I've tried converting it in the select statement, but both fail.
Well you need to do both. You need to change the property type (ideally fixing the naming at the same time to follow conventions) and change where you're setting it, probably using DateTime.ParseExact, specifying the format and probably the invariant culture.
(I'm assuming you know the format of the date in the file - I would strongly advise you to use DateTime.ParseExact rather than just letting the framework try various formats until it finds one which works.)
EDIT: Now that we've seen the file, it looks like you'd want either
DateTime.ParseExact(text, "d/M/yyyy", CultureInfo.InvariantCulture)
or:
DateTime.ParseExact(text, "M/d/yyyy", CultureInfo.InvariantCulture)
... but we can't tell which, as we don't know whether "6/4/2009" means April 6th or June 4th.
You should convert your customerRecord[1] string to a DateTime.
var csv =
from line in File.ReadAllLines("C:/file.csv")
let customerRecord = line.Split(',')
select new Customer()
{
contactID = customerRecord[0],
surveyDate = DateTime.Parse(customerRecord[1]),
project = customerRecord[2],
projectCode = customerRecord[3]
};
public class Customer
{
public string contactID { get; set; }
public DateTime surveyDate { get; set; }
public string project { get; set; }
public string projectCode { get; set; }
}

Categories

Resources