InvalidCastException when getting data from Database as a datetime - c#

I'm trying to pull some data from my Database, but when it comes to Datetime I get an
InvalidCastException
This is my Database (SQL SERVER)
Create table Timeline(
start date,
end date
)
table Timeline
start(date) end(date)
03/07/2020 NULL
NULL 10/07/2020
15/07/2020 25/07/2020
This is my query
SELECT Timeline.start, Timeline.end FROM Timeline
This is my c# code
public class Timeline
{
public DateTime? startDate;
public DateTime? endDate;
public static Timeline fromRow(DataRow r)
{
Timeline a = new Timeline();
a.startDate = r.Field<DateTime?>("start");
a.endDate = r.Field<DateTime?>("end");
return a;
}
}
When I pull a NULL value it works, but when I pull a real date like 03/07/2020 It gives me InvalidCastException. Is there a way to check the value before putting it into the variable?

This is because the column type does not match up with what you are
trying to cast to here, my guess is it is being referenced as a varchar
type constructor public DataColumn (string columnName), which is
fine we can just parse the date safely as a string, you may lose a few
cycles on this but this is not 1995 anymore. If you want to cast safely
than offer the column the correct type so constructor public
DataColumn (string columnName, Type type)
// Change Names to match C# nameing conventions
public class TimeLine
{
// Since this class is just a strongly typed object for rows of your table values should not be able to change.
public DateTime? StartDate { get; private set; }
public DateTime? EndDate { get; private set; }
public static TimeLine FromRow(DataRow r)
{
// Casting will depend if we know the DataColumns type, if we dont we can just parse the string value
return new TimeLine
{
StartDate = DateTime.TryParse(r.Field<string>("start"), out var sd) ? sd : (DateTime?)null,
EndDate = DateTime.TryParse(r.Field<string>("end"), out var ed) ? ed : (DateTime?)null,
};
}
}

Related

Linq: Transforming specified column's datatypes and values while preserving unspecified columns

I have a list of Order objects. Order has the properties: int Id, decimal Price, string OrderNumber, string ShipperState, DateTime TimeStamp;
I know which columns I want to transform (Price, TimeStamp) and I want to keep the other columns without needing to specify them.
This example is transforming specified columns but I still need to include the non-transformed columns.
var myList = model.Orders.Select(x => new
{
x.Id,
x.OrderNumber,
// decimal to string
Price = x.Price.ToString("C", new CultureInfo("en-US")),
x.ShipperState,
// DateTime to string
TimeStamp = x.TimeStamp.ToString("MM/dd/yyyy H:mm")
}
If I were to add a column string ShipperCity to the Order class, I would like myList to also have that property without having to go back and update the projection.
An ideal answer would not rely on external libraries, reflection and only be a line or two.
If you do not want to modify the model class as #David suggested you can write extension methods for it like this:
public static class OrderExtensions
{
public static string GetFormattedPrice(this Order order)
=> order.Price.ToString("C", new CultureInfo("en-US"));
public static string GetFormattedTimestamp(this Order order)
=> order.Timestamp.ToString("MM/dd/yyyy H:mm");
}
UPDATE #1
The effect of this alternative is that whereever you wanted to use the transformed order.Price and order.Timestamp there you have to use order.GetFormattedPrice() and order.GetFormattedTimestamp() respectively.
In the question it was not specified that where the data come from and what type of application the data is used in.
For example methods cannot be used in XAML binding and everywhere else where a property is required.
Please note:
In C# (almost) everything is strongly typed hence once the class and the properties in it are defined you cannot set one of its property value to a different type of data and also you cannot change the type of the property. So by default you cannot avoid projection when you need some transformation. If you need all the properties - either the original value or the transformed value - you have to list all of them in the projection.
almost everything except dynamic
You can actually transform the type and the value of a property but only if it is defined as dynamic. For example this works below:
public class Order
{
public int Id { get; set; }
public string OrderNumber { get; set; }
// Original: decimal; Converted: string;
public dynamic Price { get; set; }
public string ShipperState { get; set; }
// Original: DateTime; Converted: string;
public dynamic Timestamp { get; set; }
}
public static class OrderExtensions
{
public static void Transform(this Order order)
{
if (order.Price.GetType() == typeof(decimal))
order.Price = order.Price.ToString("C", new CultureInfo("en-US"));
if (order.Timestamp.GetType() == typeof(DateTime))
order.Timestamp = order.Timestamp.ToString("MM/dd/yyyy H:mm");
}
}
class Program
{
static void Main(string[] args)
{
var originalList = new List<Order>()
{
new Order() { Id = 1, OrderNumber = "1", Price = 100m, Timestamp = DateTime.Now },
new Order() { Id = 2, OrderNumber = "2", Price = 200m, Timestamp = DateTime.Now },
new Order() { Id = 3, OrderNumber = "3", Price = 300m, Timestamp = DateTime.Now }
};
originalList.ForEach(order => order.Transform());
}
}
Although this example works there are some things to know:
dynamic type
This example looks like a hack, maybe it can be considered as a hack. :)
In this example the original Order objects are changed not their projection/clone/etc.
dynamic properties are not allowed in Entity Framework models as you cannot specify the SQL column type for them even using the methods of DbModelBuilder. I did not try it in other use-cases but it seems to be a very restricted possibility.
For dynamic properties there is no IntelliSense, so after typing order.Price. no list would appear with any method or property.
You have to use these properties very carefully as there is no compile-time check. Any typo or other mistake will throw an exception only during run-time.
If this option somehow fits the needs it might be worth implementing the conversion of the string value back to the original type.
That's all the update I could add to my original answer. Hope this is an acceptable answer to your comment.

Stuck in loading data from database

In making this appointment calendar, I wanted to use access database to save and retrieve my appointments. However I have more than one property type (Strings, Ints, DateTime) and more than one type of boxes (ComboBox, ListBox, DateTimePicker) to display in the windows forms.
I have managed to write my code for the database with the following code (part of it):
foreach(var appointment in listOfAppointments)
{
OleDbCommand DbCommand = new OleDbCommand(
"INSERT INTO NewAppointmentDatabase " +
"([Start], [Length], [DisplayableDescription], [OccursOnDate], [Location], [IsRecurring], [Frequency], [Occurence]) " +
"VALUES(#Start, #Length, #DisplayableDescription, #OccursOnDate, #Location, #IsRecurring, #Frequency, #Occurence)",
SaveAppntAccess);
DbCommand.Connection = SaveAppntAccess;
DbCommand.Parameters.AddWithValue("#Start", appointment.Start); //is a short time in DateTime
DbCommand.Parameters.AddWithValue("#Length", appointment.Length); //is an int
DbCommand.Parameters.AddWithValue("#DisplayableDescription", appointment.DisplayableDescription); //is a long string
DbCommand.Parameters.AddWithValue("#OccursOnDate", appointment.OccursOnDate(date)); //is a boolean with DateTime as argument
DbCommand.Parameters.AddWithValue("#Location", appointment.Location); //is a string
DbCommand.Parameters.AddWithValue("#IsRecurring", appointment.IsRecurring); //is a boolean with yes/no tickbox
DbCommand.Parameters.AddWithValue("#Frequency", appointment.Frequency); //is a string
DbCommand.Parameters.AddWithValue("#Occurence", appointment.Occurence); //is an int
I have to note that the word date in appointment.OccursOnDate(date) is reddened in visual studio which is kind of weird because the boolean parameter is inherited.
And then comes the tricky part: I want to load my data! But I want to take my values from the database and assign them to each property first, and then take those and display them in the ComboBoxes and TextBoxes and DateTimePickers.
The code goes like this (part of it):
if(LoadAppntAccess.State == ConnectionState.Open)
{
OleDbCommand DbCommand = new OleDbCommand(
"SELECT * FROM NewAppointmentDatabase", LoadAppntAccess);
OleDbDataReader reader = null;
DbCommand.Connection = LoadAppntAccess;
reader = DbCommand.ExecuteReader();
foreach (var appointment in listofAppointments)
{
while (reader.Read())
{
//code to complete
}
}
}
How will I assign the values from each field to each property? I was thinking something like this:
appointment.Start.Add(reader["Start"].ToString());
appointment.Length.Add((reader["Length"].ToString());
appointment.DisplayableDescription(reader["DisplayableDescritpion"].ToString());
But I get errors in all of those - what is the right syntax?
EDIT : I forgot to mention that "start" although it's assigned as DateTime, I used as a ShortTime value because I wanted a ComboBox with time and 30 minute intervals. So it's not exactly a Date. For OccursOnDate it was written as:
public bool OccursOnDate(DateTime date)
{
return date.Date == date;
}
and to retrieve a date I used a DateTimePicker.
2nd edit for more info
My class looks like this:
public class Appointment : IAppointment
{
public DateTime Start { get; set; }
public int Length { get; set; }
public string DisplayableDescription { get; set; }
public bool OccursOnDate(DateTime date)
{
return date.Date == date;
}
//custom members
public int ID { get; }
public string Location { get; set; }
public bool IsRecurring { get; set; }
public string Frequency { get; set; }
public int Occurence { get; set; }
public Appointment()
{
}
but unfortunately it inherits the parameters from IAppointment which has this code.
int ID { get; }
DateTime Start { get; }
int Length { get; }
string DisplayableDescription { get; }
bool OccursOnDate(DateTime date);
//custom members
string Location { get; set; }
bool IsRecurring { get; set; }
string Frequency { get; set; }
int Occurence { get; set; }
The custom members are my addition since I had to put some extra stuff according to the specs.
However I managed to find a syntax based on your answers below.
appointment.Start.((DateTime)reader["Start"]);
appointment.Length.((int)reader["Length"]);
appointment.DisplayableDescription.((string)reader["DisplayableDescritpion"]);
appointment.OccursOnDate((DateTime)reader["OccursOnDate"]);
appointment.Location.((string)reader["Location"]);
appointment.IsRecurring.((bool)reader["IsRecurring"]);
appointment.Frequency.((string)reader["Frequency"]);
appointment.Occurence.((int)reader["Occurence"]);
I still get this message:
Any clues?
From the info you have given i would guess something like this:
appointment.Start = (DateTime)reader["Start"];
appointment.Length = (int)reader["Length"];
appointment.DisplayableDescription = (string)reader["DisplayableDescritpion"];
This is just a simple example, we would need more info to give a better answer. If any of the columns can have a null value you need to handle that as well etc..
access reader's columns like this, using column index (column index 0 is first column from your SELECT clause, 1 is second... etc). As you can see, call reader's right method to get appropriate type of data.
appointment.Start = reader.GetDateTime(0);
appointment.Length = reader.GetInt32(1);
appointment.DisplayableDescription= reader.GetString(2);
you can also get data by specifiying column name.
appointment.Start = reader.GetDateTime(reader.GetOrdinal("Start"));
appointment.Length = reader.GetInt32(reader.GetOrdinal("Length"));
appointment.DisplayableDescription = reader.GetString(reader.GetOrdinal("DisplayableDescritpion"));
I posted in a comment but since it wasn't made clear with no picture I am posting now my previous try that didn't work and explain.
This is the code mentioned both by #Nino and #FsDaniel
If you see the Interface's parameters (in my initial post), they only have the get property which makes Start, Length and DisplaybleDescritption readonly. Thus the error. The rest is ok because they are my custom members and I gave them get and set properties. I do not wish to make any alterations in the Interface, that's why I am asking to find if there can be another solution.

C# SQLite object custom getter and setter

I have a class which is also a table of a database SQLite.
I need to work with dates and I thought the best solution was to work with dates as integers, since in sqlite i don't have date operations. The problem is the following: I'm saving DateTime in my db as int using as helper a DateTime parameters which is ignored by db.
This is my class:
public class MYCLASS : Shared
{
[PrimaryKey]
public Guid ID { get; set; }
[Ignore]
public DateTime DATA_ORA
{
get { return DateTime.Parse(this.DATA_ORA_INT.ToString()); }
set { this.DATA_ORA_INT = int.Parse(value.ToString("yyyyMMdd")); }
}
private int DATA_ORA_INT { get; set;}
}
I'm having the app terminating without any exception when I set the DATA_ORA field. Why this code is not working?
EDIT:
this is when I got the crush:
MYCLASS transazione = new MYCLASS
{
ID = Guid.NewGuid(),
DATA_ORA = DateTime.Now
};
Looking at how you create the integer equivalent value of the datetime then, when you need to revert back to a DateTime from a such formatted field you need something like this
public DateTime DATA_ORA
{
get
{
// Add a check to return a default if DATA_ORA_INT is not initialized
return this.DATA_ORA_INT == 0 ? DateTime.Today :
DateTime.ParseExact(this.DATA_ORA_INT.ToString(),
"yyyyMMdd", CultureInfo.InvariantCulture);
}
set { ... }

When searching for a row, counts come back as 0

I am currently doing the following to try and get back a particular row in a data table based on the date time. The issue I am running into is when I run the code I continuously get 0 back for my number of rows and I am not sure as to why. Here is some code to that maybe someone can pick apart.
var myDay = new AnalysisDay(uniqueDate.Date, null, null, null, null);
var daysList = new List<AnalysisDay>();
foreach(var uniqueDate in uniqueDateList)
{
var myDay = new AnalysisDay(uniqueDate.Date, null, null, null, null);
var curDate = uniqueDate;
//This is the area where the rows come back 0 everytime "Start Date Time"
//occasionally has DBNull values
var curDatesRows = analysisInfo.Tables[0].Select("StartDateTime = #{0}#", curDate.ToShortDateString()));
}
This is the custom class that is referenced throughout,
public class AnalysisDay
{
public DateTime? Date { get; set; }
public DateTime? Time { get; set; }
public string Name { get; set; }
public int? CarNumber { get; set; }
public int? RunId { get; set; }
public ObservableCollection<AnalysisDay> Items { get; set; }
public AnalysisDay(DateTime? date, DateTime? time, string strName, int? number, int? runId)
{
Date = date;
Time = time;
Name = strName;
CarNumber = carNum;
RunId = runId;
Items = new ObservableCollection<AnalysisDay>();
}
}
If I have left anything out or need to provide more information please let me know and I will update this as soon as possible.
Something about the comparison must be failing. I usually use LINQ to query instead:
var curDatesRows
= dt.Rows.Cast<DataRow>()
.Where(x => x.Field<DateTime?>("StartDateTime").HasValue
&& x.Field<DateTime>("StartDateTime").Date == curDate.Date);
I tested this out on some sample data - it will filter out rows with a null value.
Note that if curDate has a time portion, and it doesn't match exactly what's in the table, then you won't get any rows unless you use .Date to drop the time portion when doing the comparison.
It looks like you are passing a short date string as the filter against a column containing date time values (which include time components). I believe that either no date in the startDateTime column will match the filter, or only values where the time component is all zeros (which is unlikely, unless you are modifying the times before persisting.
Without additional information, it's hard to tell. Can you show us some sample data from the table? Specifically, the startDateTime column?

Assign current date to a property in MVC

I am creating a model for users and I want that property joined was set to Now(). Here's my code:
[DefaultValue(DateTime.Now)]
public DateTime joined {get; set;}
I get error:
An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type.
What am I doing wrong? And what's the best way to do what I want?
DateTime.Now is not a constant, but a property that's computed at runtime, which is why you can't do what you're suggesting.
You can do what you're proposing with either:
public class MyClass {
public DateTime joined { get; set; }
public MyClass() {
joined = DateTime.Now;
}
}
Or:
public class MyClass {
private DateTime _joined = DateTime.Now;
public DateTime joined { get { return _joined; } set { _joined = value; } }
}
You could try this in your model class:
private DateTime _joined = DateTime.Now;
public DateTime Joined
{
get { return _joined; }
set { _joined = value; }
}
You can not set expressions to the default value attribute. Because dataannotaions are not runtime attributes. You should set default value by like this
private DateTime _joined = DateTime.Now;
public DateTime Joined
{
get {
return _joined;
}
set {
_joined = value;
}
}
You can do it like what the others suggest, but another alternative is to set it in your action method, after your mapping from view model to domain and just before adding it to the database (if this is what you need to do):
[HttpPost]
public ActionResult Create(YourViewModel viewModel)
{
// Check if view model is not null and handle it if it is null
// Do mapping from view model to domain model
User user = ... // Mapping
user.DateJoined = DateTime.Now;
// Do whatever else you need to do
}
Your domail model for user:
public class User
{
// Other properties here
public DateTime DateJoined { get; set; }
}
I personally would have set it in the action method because the date and time would be more closer to when the user is actually added to the database (assuming this is what you wanted to do). Let says you create your user object at 12:00 then this will be your time when the user is added to the database, but what if you only click the submit button at 12:30? I would much rather prefer 12:30 than 12:00.

Categories

Resources