NHibernate - Why my DateTime property is always DateTime.MinValue - nhibernate

I have a [DataContract] called ReportRequest with a
NOT NULL column 'SubmittedAt'. So my DataContract looks something like:
[DataContract]
public class ReportRequest
{
Int32 templateId;
DateTime submittedAt = DateTime.Now;
[DataMember]
public virtual Int32? Id
{
get;
set;
}
public virtual DateTime SubmittedAt
{
get {
return submittedAt;
}
set
{
submittedAt = value;
}
}
}
Because, I have taken a private variable submittedAt and is initialised with DateTime.Now,
shouldn't the SubmittedAt property have the same value??
But when i am calling NHibernate
session.Save(objReportRequest);
I am getting the error:
SqlDateTime overflow. Must be between 1/1/1753 12:00:00 AM and 12/31/9999 11:59:59 PM.
Any thoughts why I am getting this error?
As a workaround for now I have changed getter for SubmittedAt property as:
get {
if (submittedAt == DateTime.MinValue)
return DateTime.Now;
else
return submittedAt;
}

SQL Server minimum DateTime value is bigger than DateTime.Min value. So you cannot save minimum value to database.

As Marek Tihkan already said: SqlServer can not store the DateTime.MinValue, it is outside of the value range of SqlServer's DateTime data type.
The best advise is to use nullable types anyway:
[DataContract]
public class ReportRequest
{
DateTime? submittedAt = null;
public virtual DateTime? SubmittedAt
{
get {
return submittedAt;
}
set
{
submittedAt = value;
}
}
}
By SubmittedAt.HasValue you know if it is actually set to something reasonable. You shouldn't depend on some "magic values" to decide if a value is initialized or not.

It's because DateTime.MinValue doesn't have the same meaning as the minimum value you could store in a SQL Server datetime column. In SQL server datetime column the minimum date you could store is the one you get in your exception stack. It is SqlDateTime.MinValue

Related

Allow Invalid Date in ASP.NET Core API Service

We have a API service that we are updating and we converted some date objects from strings to DateTime objects. In the old code we tested the string if it would parse to a data time or not. If it was a bad formatted string, it would assign DateTime.Min and continue on. Now customers are sending in bad dates and it blows up since the serialization happens outside our code (MVC Controller). I am trying to find some way that when serializing a DateTime object, if it can not parse it, it just returns DateTime.Min instead of blowing up the call.
Here is the response from the API Call.
{
"date": [
"Could not convert string to DateTime: Invalid Date. Path 'date', line 3, position 24."
]
}
===== UPDATE =====
I finally found somewhere that recommended a custom JsonConverter. I finally got something that works, but there is little out there so if there is something I could do better I am all ears.
Custom Converter
public class DateConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
try
{
return DateTime.Parse(reader.Value.ToString());
}
catch (Exception ex)
{
return DateTime.MinValue;
}
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(DateTime);
}
}
DTO Class
public class Request
{
[JsonConverter(typeof(SafeDateConverter))]
public DateTime Date { get; set; }
}
Another approach introduce another property on deserialized class of type DateTime? and leave original string property as it is.
public class Request
{
public string Date { get; set; }
private DateTime? _parsedDate;
[JsonIgnore]
public DateTime? ParsedDate
{
get { return _parsedDate; }
set
{
if (DateTime.TryParse(value, out DateTime parsed)
{
_parsedDate = parsed;
return;
}
_parsed = null;
}
}
}
But having custom serializer looks better, because you need change nothing in the code which already uses deserialized object.
Suggestion:
Do not use try ... catch for serializing bad formatted dates, there is DateTime.TryParse method which will do this without throwing exceptions.
And if it is not late, you can use Nullable<DateTime> instead of having DateTime.Min as "not existing" value.

How do I make a datamember optional on deserialization?

I have an xml where the TimeStamp is not set. I have tried every possible combination here but on deserialization it always throws an exception with: There was an error deserializing the object of type MyType. The value '' cannot be parsed as the type 'DateTime'.
[DataMember(IsRequired = false, EmitDefaultValue = false)]
public DateTime TimeStamp = DateTime.Now;
What exactly do I need to set on this TImeStamp member so that it is optional on deserialization (=not needed to be in the xml)
EDIT: What I tried on Xaruth's suggestion:
[DataMember]
[DefaultValue(typeof(DateTime), "2014-08-25T09:31:09.2477328+02:00")]
public DateTime TimeStamp { get; set; }
public bool ShouldSerializeTimeStamp()
{
return TimeStamp != null;
}
public void ResetTimeStamp()
{
TimeStamp = DateTime.Now;
}
You can use methods SouldSerialize and Reset, wich can be define for any properties.
For a property called TimeStamp, you can write methods SouldSerializeTimeStamp and ResetTimeStamp
According to MSDN, ResetTimeStamp will give you a default value for TimeStamp and SouldSerializeTimeStamp will be used to serialize or not TimeStamp.

MVC4 Dynamic DateTime Value in Model

I am trying to generate a dynamic warranty end date by calculation and I am getting nowhere with intelisense. Any help will be appreciated.
public int WarrantyMonths {get;set;}
public DateTime DateIn{get;set;}
public DateTime? WarrantyEnd
{
get { return DateTime.AddMonths(this.DateIn((this.WarrantyMonths))); }
}
You should read the documentation on how to use a function if you don't understand it. What you probably want is:
get { return DateIn.AddMonths(WarrantyMonths); }

Persisting Part Record to Database

Working on creating my first Orchard Module and I am running into issues getting the form data saved back to the database. I have everything registered correctly as far as I can tell from looking at a lot of samples so I must be missing something minor.
I am able to get the Apartment form to show under the new menu, validation is working but when I fill the form completly and hit save I get:
Your Apartment has been created.
Checking the database the record is not in the table and checking the logs shows:
2013-12-19 09:15:23,416 [19]
NHibernate.Transaction.ITransactionFactory - DTC transaction prepre
phase failed NHibernate.Exceptions.GenericADOException: could not
execute batch command.[SQL: SQL not available] --->
System.Data.SqlClient.SqlException: Cannot insert the value NULL into
column 'FloorPlanName', table
'Orchard.dbo.CommunityWebsiteSolutions_ApartmentPartRecord';
column does not allow nulls. INSERT fails.
Running SQL Profiler shows an insert with all columns being set to NULL.
Migrations.cs
SchemaBuilder.CreateTable(typeof(ApartmentPartRecord).Name, table => table
.ContentPartRecord()
.Column<string>("FloorPlanName", c => c.WithLength(25).NotNull())
.Column<string>("FullAddress", c => c.WithLength(256).NotNull()))
.Column<string>("ShortDescription", c => c.WithLength(150).NotNull())
.Column("NumberOfBedrooms", DbType.Int32, c => c.NotNull())
.Column("NumberOfBathrooms", DbType.Int32, c => c.NotNull())
.Column("SquareFootage", DbType.Int32, c => c.NotNull())
.Column("WhenAvailable", DbType.DateTime)
.Column("RentAmount", DbType.Decimal)
);
ContentDefinitionManager.AlterPartDefinition(typeof (ApartmentPart).Name, part => part.Attachable());
ApartmentPart
public class ApartmentPartRecord : ContentPartRecord {
public virtual string FloorPlanName { get; set; }
public virtual string ShortDescription { get; set; }
public virtual string FullAddress { get; set; }
public virtual int? NumberOfBedrooms { get; set; }
public virtual int? NumberOfBathrooms { get; set; }
public virtual int? SquareFootage { get; set; }
public virtual DateTime? WhenAvailable { get; set; }
public virtual decimal? RentAmount { get; set; }
}
public class ApartmentPart : ContentPart<ApartmentPartRecord> {
[Required, StringLength(256)]
[Display(Name = "Address / Unit Number")]
public string FullAddress {
get { return Record.FullAddress; }
set { Record.FullAddress = value; }
}
[Required, StringLength(25)]
[Display(Name = "Floor Plan")]
public string FloorPlanName {
get { return Record.FloorPlanName; }
set { Record.FloorPlanName = value; }
}
[Required, StringLength(150)]
[Display(Name = "Sales Description")]
public string ShortDescription {
get { return Record.ShortDescription; }
set { Record.ShortDescription = value; }
}
[Required]
[Display(Name = "Bedroom Count")]
public int? NumberOfBedrooms {
get { return Record.NumberOfBedrooms; }
set { Record.NumberOfBedrooms = value; }
}
[Required]
[Display(Name = "Bathroom Count")]
public int? NumberOfBathrooms {
get { return Record.NumberOfBathrooms; }
set { Record.NumberOfBathrooms = value; }
}
[Required]
[Display(Name = "Square Footage")]
public int? SquareFootage {
get { return Record.SquareFootage; }
set { Record.SquareFootage = value; }
}
[Display(Name = "First Availability")]
public DateTime? WhenAvailable {
get { return Record.WhenAvailable; }
set { Record.WhenAvailable = value; }
}
[Display(Name = "Rent Amount")]
public decimal? RentAmount {
get { return Record.RentAmount; }
set { Record.RentAmount = value; }
}
}
Driver
public class ApartmentPartDriver : ContentPartDriver<ApartmentPart>
{
protected override string Prefix
{
get { return "Apartment"; }
}
//GET
protected override DriverResult Editor(ApartmentPart part, dynamic shapeHelper)
{
return ContentShape("Parts_Apartment_Edit",
() => shapeHelper.EditorTemplate(
TemplateName: "Parts/Apartment",
Model: part,
Prefix: Prefix));
}
//POST
protected override DriverResult Editor(ApartmentPart part, IUpdateModel updater, dynamic shapeHelper)
{
updater.TryUpdateModel(part, Prefix, null, null);
return Editor(part, shapeHelper);
}
}
Handler
public class ApartmentPartHandler : ContentHandler {
public ApartmentPartHandler(IRepository<ApartmentPartRecord> repository)
{
Filters.Add(StorageFilter.For(repository));
}
}
Your error message explains this pretty clearly:
System.Data.SqlClient.SqlException: Cannot insert the value NULL into column 'FloorPlanName', table 'Orchard.dbo.CommunityWebsiteSolutions_ApartmentPartRecord'; column does not allow nulls. INSERT fails.
Your problem occurs because:
You are using nullable types such as string and int? types in your Record class, which means you want to allow nulls.
Yet, you are specifying in your DB migration that you want to disallow nulls.
And when C# instantiates your Record class, it initializes the fields using the default value, which is null for nullable types.
You can do one of the following:
Make your DB columns nullable (remove NotNull)
Make your Record class use non-nullable types (for example, int instead of int?). Note that this is not an option for reference types such as string.
Give non-null default values to the fields of your Record class by giving the class a constructor. This is arguably bad practice since you will be calling virtual properties in a base class, but seems to be ok in NHibernate.
Give non-null default values to the fields of your Record class by giving your part an OnInitializing handler, which would be placed in your Handler class.
UPDATE
You commented that you are expecting the fields to be filled in by the TryUpdateModel in the Editor function of your driver class. This does eventually happen, but the actual sequence of events that occurs is this (you can see this in the CreatePOST method of Orchard.Core.Contents.Controllers.AdminController):
ContentManager.New() with the content type ID to create content item in memory. This step calls OnInitializing for the appropriate content parts for the content type, which are defined in handlers.
ContentManager.Create() with the content item in Draft Mode. This step actually tries to persist the item to the DB once.
ContentManager.UpdateEditor(). This is the call that actually calls Editor of the appropriate driver for the content type.
Check the ModelState and roll back the transaction if anything has failed.
Step 2 will fail if you have NULL values in columns marked NotNull, because the fields have default values at that point. For these columns, you have to fill them in before step 2 by using OnInitializing or by using a constructor on your Record part.
In other words, TryUpdateModel in your driver is actually applying changes directly to the entity that has already been Created and is now attached to the NHibernate session.

Automapper resolveusing not returning nulls

I'm working on an MVC 4 project and trying to convert a value in a KeyValue list to a nullable DateTime. I have used the following line in the mapper (I've not included the other properties as there are a lot)
.ForMember(d => d.Deadline, m => m.ResolveUsing<DeadlineResolver>())
My resolver looks like this:
public class DeadlineResolver : ValueResolver<Booking, DateTime?>
{
protected override DateTime? ResolveCore(Booking source, ResolutionResult resolutionResult)
{
KeyValue keyValue = source.KeyValues.FirstOrDefault(k => k.Key.KeyId == "DEADLINE");
return (keyValue != null) ? DateTime.Parse(keyValue.Value) : (DateTime?)null;
}
}
The value of deadline which is defined as shown below is never returned as null but DateTime.MinDate instead. I need it to be null when I'm the binding the result in a view so that I only show a value when there is a date.
public DateTime? Deadline { get; set; }
How do I make these values null without going over the values after mapping to look for min dates and set to null (temp hack I've put in place so the code runs)?
Using LinqPad and AutoMapper 2.2.1 the following gives me a valid date when KeyValue has a date, and a null DateTime when KeyValue is null. (Note there are minor changes to the resolver to simplify it as the class definitions weren't provided).
void Main()
{
AutoMapper.Mapper.CreateMap<Booking, dest>()
.ForMember(d => d.Deadline, m => m.ResolveUsing<DeadlineResolver>());
AutoMapper.Mapper.AssertConfigurationIsValid();
// Gives a valid DateTime
var booking = new Booking { KeyValue = "2013-01-01" };
booking.Dump();
var rc = AutoMapper.Mapper.Map<Booking, dest>(booking);
rc.Dump();
// Gives a null DateTime
booking = new Booking { KeyValue = null };
booking.Dump();
rc = AutoMapper.Mapper.Map<Booking, dest>(booking);
rc.Dump();
}
// Define other methods and classes here
public class Booking
{
public string KeyValue { get; set; }
}
public class dest
{
public DateTime? Deadline { get; set; }
}
public class DeadlineResolver : AutoMapper.ValueResolver<Booking, DateTime?>
{
protected override DateTime? ResolveCore(Booking source)
{
return (source.KeyValue != null)
? DateTime.Parse(source.KeyValue)
: (DateTime?)null;
}
}
Is this the functionality you were after? If so, then the issue could be with an older version of AutoMapper, or an unexpected KeyValue value.