Out-of-range Datetime issue using LINQtoSQL - asp.net

Here is a snippet of my model:
* SQL Server *
Event
-----
Id (int, PK) NOT NULL
Title (varchar(100)) NULL
LastModified (datetime) NULL
EventDates
----------
Id (int, PK) NOT NULL
StartDate (datetime) NULL
EndDate (datetime) NULL
* C# *
Event
-----
public int Id
public string Title
public DateTime LastModified
EventDates
----------
public int Id
public DateTime StartDate
public Datetime EndDate
The LastModified field has been in the database since its creation. I have been saving it's value when I save an event, but I want to display it in a table, so I changed up my Event repository's GetEvents's return value:
return (from e in GetDbEvents()
select new Event
{
// Miscellaneous fields..
LastModified = e.LastModified.GetValueOrDefault() // Shiny new code
});
When I call this now, I get yelled at:
The conversion of a char data type to a datetime data type
resulted in an out-of-range datetime value.
If I strip down the above code to this, it still doesn't help and I get the same error if I attempt to enumerate over the result:
var test = (from e in _db.Events
select e.LastModified.GetValueOrDefault());
As a test, I did the same statement for my EventDates table (again, with 2 datetime columns):
var test4 = (from ed in _db.EventDates
select new EventDate
{
StartDate = ed.StartDate.GetValueOrDefault(),
EndDate = ed.EndDate.GetValueOrDefault()
});
This works fine, of course. No errors when I enumerate, all values are correct.
I should point out that some LastModified values in the Events table are NULL while all values in EventDates are populated with data.
Edit
My main question is why does Events give me out-of-range issues and EventDates does not, even though the model is quite similar?

The problem is with the GetValueOrDefault(). This will return DateTime.MinValue (01-01-0001) in the "default" case and sqlserver doesn't accept that (it refuses dates before about 1753).
If you use a Nullable<DateTime>, then sql will be happy with the null it will get.

If you change the declaration of your C# variable LastModified to public DateTime? LastModified that should fix your problem. The addition of the question mark indicates that it is a nullable type.

Perhaps this is related?
One possible fix (if you don't want to change LastModified to DateTime?, requiring you to litter your code with .Value everywhere..) would be to get the values from the Database as DateTime?'s, but translate them to DateTime's in your code. For example:
return from e in GetDbEvents()
select new Event(e.LastModified);
...
//In Event class:
public Event(DateTime? lastModified)
{
LastModified = lastModified.GetValueOrDefault();
}
This will cause GetValueOrDefault() to be called client-side, rather than being part of the SQL.
Note that this approach does have problems of its own...

To expand on Hans Kesting's answer, and help people who encounter this issue in a filtering/WHERE clause rather than in the SELECT clause:
The problem is with the GetValueOrDefault() call. LINQ to SQL (somewhat stupidly IMO) translates this to real SQL by using a COALESCE clause (similar to ISNULL) like this:
COALESCE(LastModified, '1/1/0001 12:00:00 AM')
Unfortunately, the SQL Server datetime data type doesn't accept dates before about 1753. So SQL Server throws the error back to LINQ to SQL, which throws it back to you.
If you use a Nullable, then SQL Server will be happy with the null it will get. But if you still want to use GetValueOrDefault(), e.g. in a query to filter the results you have a couple options:
LastModified.GetValueOrDefault(System.Data.SqlTypes.SqlDateTime.MinValue.Value)
LastModified.HasValue && LastModified > DateTime.Now

Related

Entity Framework throws DBUpdateConcurrencyException if table has trigger defined

While working on RESTful service in ASP.NET with support of Entity Framework and SQL Server 2014 as DB engine, I met some unexpected obstacle on my way.
I've prepared some dummy case which exactly explains what kind of problem I'm facing right now.
I've got database in SQL Server named "dummy_database". It contains only one table - Person - and one trigger - TR_Person_Insert - which is responsible for adding some extra data (current date and time in this particular case) to every single record before add the record to the "Person" table. Code of database structure is included below:
USE dummy_database;
GO
IF EXISTS (SELECT * FROM sys.tables WHERE name LIKE 'person')
DROP TABLE [person];
GO
IF EXISTS (SELECT * FROM sys.triggers WHERE name LIKE 'TR_Person_Insert')
DROP TRIGGER [TR_Person_Insert];
GO
CREATE TABLE [dbo].[person]
(
[id] INT IDENTITY(1, 1),
[name] NVARCHAR(256) NOT NULL,
[surname] NVARCHAR(256) NOT NULL,
[age] INT NOT NULL,
[valid_from] DATETIME2 (7),
CONSTRAINT PK_Person_ID PRIMARY KEY ([id])
)
GO
CREATE TRIGGER TR_Person_Insert ON [dbo].[person]
INSTEAD OF INSERT
AS
BEGIN
INSERT INTO [dbo].[person] ([name], [surname], [age], [valid_from])
SELECT i.[name], i.[surname], i.[age], GETDATE()
FROM inserted i
END
GO
Then I created dummy RESTful Service using ASP.NET and added Entity Framework with Database First approach to it. The code of one and only existing method of that Service is as below:
[RoutePrefix("api/person")]
public class DummyController : ApiController
{
[HttpPost, Route("")]
public IHttpActionResult AddPerson(PersonDto data)
{
using (var ctx = new DummyDatabaseModel())
{
var person = new person
{
name = data.Name,
surname = data.Surname,
age = data.Age
};
ctx.people.Add(person);
ctx.SaveChanges();
}
return Ok();
}
}
The problem is with saving changes method called on database's context. This method throws DBUpdateConcurrencyException exception with additional information as below:
An exception of type 'System.Data.Entity.Infrastructure.DbUpdateConcurrencyException' occurred in EntityFramework.dll but was not handled in user code
Additional information: Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded.
I'm aware of the fact that this exception is caused by trigger, which is fired instead of regular 'insert' routine on database's side.
The question I ask is - how to get rid of this kind of exception without abandoning trigger in database. I'm out of ideas and to be frank - I count on your tips or solutions, if you faced same problem in the past.
Thank you for your help.
I had the same problem, in my case, the trigger was the expression "instead of insert"
Ex.
Create trigger TR_AA on TbAA INSTEAD OF INSERT ....
I changed the expression by "AFTER INSERT" It's work for me!
Ex.
Create trigger TR_AA on TbAA AFTER INSERT ....

PetaPoco \ NPoco - Partial Insert

Is there a way to partially insert an object using PetaPoco or NPoco?
For example, I have a table called Users:
UserId | UserName | UserMail | UserCreationDate
Each one of these columns are NON NULLABLE and have a default value when they are left empty.
In ASP.NET I have a User class, and I use the ORM to insert a new record with only the name:
Dim userData As New User()
userData.UserName = "Jimmy Hendrix"
db.Insert(userData)
I expect the database to look as follows:
UserId | UserName | UserMail | UserCreationDate
12 | Jimmy Hendrix | (DB default)| (DB default)
I want the insert command only insert the name, without inserting the other object properties with the object's default values.
Such as there is a partial update, I want a partial insert.
Is that possible in PetaPoco?
Is there another way to do it by myself without any ORM?
Edit:
Using SQL I can get the job done, but I need to use POCO objects, so I don't want to have to remember the database parameters. I want something like
user.UserName = "Michael"
user.Insert(user)
And it will insert only the UserName, ignoring the other variables. The SQL that I want to be generated in the background is:
"INSERT Users(UserName) VALUES(#UserName)"
(while the #UserName parameter holds the userData.FirstName value)
As you can see, it doesn't take in account the other variables in the class.
Today if I use the insert command, even if I give a value to a single property in the class, NPoco still tries to insert ALL the class variables into the db setting the variables I didn't want to set with the class's default values (which are different from the db default values)
Also, all of the properties are insertable/updateable, so there can't be any ResultColumn types in the class. I want to insert these values but only the ones I declare in that particular instance. All of the properties are available to update and insert but for each instance i insert only what i declare.
I would create a PartialUserForInsert class:
[TableName("Users")]
public class PartialUserForInsert
{
public string UserName { get; set; }
}
Your provided schema does not include a FirstName column.
Assuming the column is mapped to UserName, using the following should insert as expected.
dim sql = new Sql("INSERT Users(UserName) VALUES(#0)", userData.FirstName)
db.Execute(sql)

Entity Framework 5: Using DatabaseGeneratedOption.Computed option

I have an EF5 code first project that uses the [DatabaseGenerated(DatabaseGeneratedOption.Computed)] attribute.
This option is overriding my settings.
Consider this SQL table:
CREATE TABLE Vehicle (
VehicleId int identity(1,1) not null,
Name varchar(100) not null default ('Not Set')
)
I am using the SQL default construct to set the [Name] is case it is not set.
In code behind, I have a class defined similar to:
public class Vehicle {
...
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public string ShoulderYN { get; set; }
}
When I update the entity in code, the value set in the default overrides my new setting.
In code, I have (pseudo):
vehicle.Name = 'Update Name of Vehicle';
_dbContext.Update(vehicle);
_dbContext.SaveChanges();
The expected result is Vehicle.Name = 'Update Name of Vehicle'.
The actual result is Vehicle.Name = 'Not Set'.
Is there a way in EF5 to say:
if Vehicle.Name is null/empty, use the value defined in the database? Otherwise, if I set the value in code, I want to use this value.
Apparently, no there isn't. It's not that smart :)
As you may already read, Computed option just tells the EF not to update your column, because you will compute a value on the DB-side yourself. EF will then just return newly computed value from your database (which in your case is "Not Set").
Your basic three options are - as per EF Source code documentation:
None - The database does not generate values.
Identity - The database generates a value when a row is inserted.
Computed - The database generates a value when a row is inserted or updated.
https://github.com/aspnet/EntityFramework6/blob/527ae18fe23f7649712e9461de0c90ed67c3dca9/src/EntityFramework/DataAnnotations/Schema/DatabaseGeneratedOption.cs
Since you expect a little more custom logic to be done, I'm afraid you would have to do it yourself. I would suggest you stop relying on database default constraint and do everything in code first approach. This way you would have a code like that:
public class Vehicle
{
public Vehicle()
{
this.Name = "Not set";
}
// Without 'Generated' attribute
public string Name { get; set; }
}
This way, when your Entity is created, it automatically starts with expected default value. And can be later changed by simply modifying the Name property.
Hope it helps!
Actually there is a simple solution for this:
You need to leave default constraint with value in table creation script as it is now:
CREATE TABLE Vehicle (
VehicleId int identity(1,1) not null,
Name varchar(100) not null default ('Not Set')
)
Just remove DatabaseGenerated attribute from property in class definition:
public class Vehicle {
...
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public string ShoulderYN { get; set; }
}
And that's it: now database will use default value only if you do not specify some value in code. Hope this helps.
I checked this for hours to get good answer but no:
EF cannot update models by automatic generated-ID.
You have 3 options:
Adding another VehicleId to Vehicle model.
Change automatic generated-ID to be manual generated by you.
Setting unique identifier to be something else then the generated-ID in your
model.
In your Vehicle Class it can be the Name property.
I suggest you option 3:
Setting up unique-id to be Vehicle.Name (and you can add more properties).
Then: if vehicle by unique-id doesn't exists, add new vehicle to db-context:
//if there is no such a Vehicle in system, add it:
if (vehicle.Name !=null && vehicle.Name != String.Empty && _dbContext.Where(v => v.Name == vehicle.Name).FirstOrDefault() == null)
_dbContext.Add(vehicle);
_dbContext.SaveChanges();

ServiceStack ORMLite - Select columns

I recently started working with ServiceStack and its ORMLite framework. I have searched on Google and browsed the source code but couldn't find anything relevent.
Is there any way to select specific columns when executing a query with ORMLite ?
Something like that : Db.First<Model>(q => q.Id == someId, "Column1, Column2")
Unless I missed this feature, I am surprised nobody asked about this before, since this is one the rule of thumbs to optimize your DB transactions.
If you want to specify columns other that the table you need to use SQL as seen in this earlier example
So in your case you could do something like:
Db.First<Model>("SELECT Column1, Column2 FROM AnyTableOrView");
You can also create a partial model that looks at your table by decorating it with the [Alias] attribute, like:
[Alias("AnyTableOrView")]
public class Model {
public int Id { get; set; }
public string Column1 { get; set; }
public string Column2 { get; set; }
}
Then you can do something like:
Db.First<Model>(q => q.Id == someId);
And it will only SELECT + populate fields from the partial model.
I did try this :
Created a Database VIEW (table name and columns are already set)
Created a class named "Event" and matching each fields for that table with a property
(i used [Alias] for table name and for all columns to have nice names)
Wrote access to DB to select 1 record based on it's ID
var dbFactory = new OrmLiteConnectionFactory(
"Data Source=MyDB;User Id=user;Password=pwd", // Connection String
OracleDialect.Provider);
using (var db = dbFactory.OpenDbConnection())
{
var event = db.GetByIdOrDefault<Event>( request.Id );
}
At that point the var 'event' is populated but only the Id field is filled !
all the others fields of the class are not filled (while there are really data in database).
It's the simplest i can do and it does not work. Any ideas ?
(PS : i am using OrmLite for Oracle)
Thanks
I have found the problem.
It was due to an incorrect type matching between field in my class (defined as a string) and the corresponding Oracle Field (that is a DATE).
I replaced the string with datetime and worked like a charm.
So it's working perfectly with a VIEW and that's GREATLY simplify the code.
I had a similar problem, however my solution was different.
I had a int property in my POCO. My query (from Oracle) was returning a null for this property. It caused a exception to be raised and prevented further processing of that row.
The result was a partial populated POCO.
The solution was to change to type to be nullable.
public int? mypropperty

How to pass null as DateTime value?

In a database, there is a field that saves a closure date. This date can be NOT NULL only if the case has been closed. If the case is not closed, it has to be NULL. How can I pass null value to a DateTime object?
Tried this but it doesn't work.
DateTime closure= dateDatumIspisa.SelectedDate ?? null;
DateTime closure= dateDatumIspisa.SelectedDate ?? DateTime.Parse("");
DateTime closure= dateDatumIspisa.SelectedDate ?? DBNull.Value;
DateTime closure= dateDatumIspisa.SelectedDate ?? DateTime.Parse(DBNull.Value.ToString());
Also tried GetValueOrDefault() but it inserts DateTime.Min value, while I need this field left empty.
Any suggestions?
Just make closure a DateTime? instead of a DateTime. The whole point of Nullable<T> is to make a nullable type from a non-nullable one.
Now, you haven't shown the type of SelectedDate - but if it's already a DateTime? then you don't need to use ?? at all. Just:
DateTime? closure= dateDatumIspisa.SelectedDate;
How familiar are you with nullable value types in general? You might want to read up on the MSDN coverage of them.
Declare
DateTime ? closure = dateDatumIspisa.SelectedDate;
no need here to use the ?? in this line !

Resources