Having this Postgres table
CREATE TABLE "TimeRange"
(
"Id" bigint NOT NULL,
"Start" timestamp with time zone NOT NULL,
"End" timestamp with time zone NOT NULL,
CONSTRAINT "TimeRange_pkey" PRIMARY KEY ("Id")
)
Filling it with
INSERT INTO "TimeRange"("Id", "Start", "End")
VALUES (1, '2014-03-03 05:55:00+01', '2014-03-03 05:55:00+01');
Having this C# POCO
public class TimeRange
{
public long Id { get; set; }
public DateTimeOffset Start { get; set; }
public DateTimeOffset End { get; set; }
}
Selecting data..
myOpenedNpgsqlConnection.Query<TimeRange>("SELECT * FROM \"TimeRange\"");
..results in DataException: "Error parsing column 1 (Start=03.03.2014 05:55:00 - DateTime)"
I'm aware that the timezone information is missing in the error message. Running the same sql statement in PgAdmin returns results that include timezone information.
Is there any (clean) way to read Postgres "timestamp with timezone" via dapper into DateTimeOffset?
EDIT:
I've found that modifing the POCO to following seems to work. But seriously, is there no nicer way?
public class TimeRange
{
public long Id { get; set; }
private DateTime Start { get; set; }
private DateTime End { get; set; }
public DateTimeOffset StartOffset { get { return this.Start; } set { this.Start = value.UtcDateTime; } }
public DateTimeOffset EndOffset { get { return this.Start; } set { this.Start = value.UtcDateTime; } }
}
It seems that PostgreSQL stores internally the UTC timestamp only.
Maybe it helps, if you cast the timezone information in SQL query, PostgreSQL uses the TZ from client session.
CREATE TABLE TimeRange
(
Id bigint NOT NULL,
start_at timestamp with time zone NOT NULL,
end_at timestamp with time zone NOT NULL,
CONSTRAINT TimeRange_pkey PRIMARY KEY (Id)
);
INSERT INTO TimeRange (Id, start_at, end_at)
VALUES (1, '2014-03-03 05:55:00+01', '2014-03-03 05:55:00+01');
SELECT
id,
start_at::TIMESTAMP WITH TIME ZONE as start,
end_at::TIMESTAMP WITH TIME ZONE as end
FROM TimeRange;
Related
I create a table with primary key.
I tried to insert new data with entityframework6, but it would get 23502 error.
But I add the default value to the column before I insert it.
I don't understand why it would get this error.
Table DDL:
CREATE TABLE ERRORLOG(
id numeric NOT NULL,
message varchar(50) NULL,
CONSTRAINT pterrorlog_pk PRIMARY KEY (id)
);
Model:
public partial class ERRORLOG
{
[Key]
[Column(Order = 0)]
public long ID { get; set; } = DateTimeOffset.Now.ToUnixTimeMilliseconds();
public string MESSAGE { get; set; }
}
Funcation:
using (DbContext Db as new DbContext)
using (TransactionScope transactionScope = new TransactionScope())
{
ERRORLOG iLog = new ERRORLOG();
iLog.MESSAGE = Message;
Db.ERRORLOG.Add(iLog);
Db.SaveChanges(); //Get 23502 error
}
Here is the insert script, it looks like didn't insert the id, why is that?
INSERT INTO "pterrorlog"("message") VALUES (#p_0) RETURNING "id"
Edit:
After I add this script on the Model, it works fine now.
public partial class ERRORLOG
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public long ID { get; set; } = DateTimeOffset.Now.ToUnixTimeMilliseconds();
public string MESSAGE { get; set; }
}
Looks like Entity Framework auto insert a value to the column.
After I add the script to prevent this issue, it works fine now.
[DatabaseGenerated(DatabaseGeneratedOption.None)]
Model would like:
public partial class ERRORLOG
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public long ID { get; set; } = DateTimeOffset.Now.ToUnixTimeMilliseconds();
public string MESSAGE { get; set; }
}
You can use PGAdmin to profile the SQL that EF is actually attempting to execute on SaveChanges. C# is case sensitive while Postgres defaults to lower case. If I recall NPGSQL will format all EF SQL Queries with double-quotes so if your Entities were declared with properties like ID, it would be generating statements like INSERT INTO "ERRORLOG" ( "ID", "MESSAGE" ) VALUES ( ... ) so a column named "id" wouldn't be getting set.
If you want your entities to use a different case than the DB, and leave Postgres using lower case then I'd recommend using [Column] attributes to rename the columns:
public partial class ERRORLOG
{
[Key, Column(Name = "id")]
public long ID { get; set; } = DateTimeOffset.Now.ToUnixTimeMilliseconds();
[Column(Name = "message")]
public string MESSAGE { get; set; }
}
The other detail is that Order on the Column attribute is only needed when dealing with composite keys, such as many-to-many joining tables where the PK is made up of two or more columns. It isn't needed for normal single-value PKs.
If that isn't the cause, checking the insert statement in PGAdmin should give you a clue what NPGSQL / EF is attempting to execute.
I am using EF Core to handle my database and I want insert a record to my postgresql database.
But when I insert the record and do the save changes, the app throws an error:
Index was out of range. Must be non-negative and less than the size of
the collection. (Parameter 'index')
I understand that this error can appear when I work with collections but in this case I am doing an insert.
my base entity:
public abstract class BaseEntity: IBaseEntity<long>
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key, Column("id", Order = 0)]
public long Id { get; set; }
[Column("createdAt")]
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime CreatedAt{ get; set; }
}
the insert from business layer:
var entityToInsert = new Delivery()
{
State = 1, //active
Date = DateTime.Now,
};
await this.UOW.Delivery.CreateAsync(entityToInsert);
await this.UOW.SaveChangesAsync();
My generic repository method:
public async Task<TEntity> CreateAsync(TEntity entity)
{
await Context.AddAsync<TEntity>(entity);
return entity;
}
the delivery table definition:
id bigint // type identity "by default" autoincrment 1 per 1
state integer
date timestamp without time zone (not null)
createdAt timestamp without time zone (not null)
What am I doing wrong?
I have two or more application and can`t update in one time
I want use type INT in model in one new application with field "Serial" with type SMALLINT in db and it must work when type in DB will changed to INT in feature without exception in new code
For old applications
public class Machine
{
public int Id { get; set; }
public short Serial { get; set; }
...
}
For new applications
public class Machine
{
public int Id { get; set; }
public int Serial { get; set; }
...
}
Here where can be exeption
var machine = context.Machines.FirstOrDefault(o => o.id == id);
When i use Stored Procedures it type indepeded, i what do same
var serial = Convert.ToInt32(sqlDataReader["Serial"]);
Add view in database with cast to SMALLINT or INT if you want to increase or decrease maximum value for choosed field
Sample:
CREATE VIEW [dbo].[view_machines]
AS
SELECT id, CAST(serial AS INT) AS serial
FROM dbo.machines
now you can change in table machines field type to INT or SMALLINT anytime
I'm new to EF and trying database-first. Looks mostly ok but there are a few cases where model class fields aren't quite what I want.
Table columns ...
[Id] uniqueidentifier NOT NULL PRIMARY KEY,
[InsertedAt] datetime NOT NULL DEFAULT GETDATE(),
[UpdatedAt] datetime NOT NULL DEFAULT GETDATE()
Model fields ...
public Guid Id { get; set; }
public DateTime InsertedAt { get; set; }
public DateTime UpdatedAt { get; set; }
My model fields are all non-nullable so they will all get default values. Instead, I want Id to be nullable, and to get a constraint violation on an attempt to insert a null ID, because I want the client to have to create GUIDs. And I want timestamps to be autogenerated by the database for datetime columns on attemtpts to insert null.
I could hand-edit every model class to get what I want, but I'm wondering if there's a better way.
Thanks!
I have a problems with Entity Framework (V 6.1.0), if a DateTime property is annotated as a Date type:
Column(TypeName = "Date")
public class MyTable
{
[DatabaseGenerated]
public Guid Id { get; set; }
[Key, Column(TypeName = "Date"), DataType(DataType.Date)]
public DateTime DateKey { get; set; }
[Required]
public string SomeProperty { get; set; }
}
The column of my table in SQL is created correctly as a DATE column.
Now if I try to insert a new row and set my date property e.g. with DateTime.Now() I get following error:
Store update, insert, or delete statement affected an unexpected
number of rows (0). Entities may have been modified or deleted since
entities were loaded. Refresh ObjectStateManager entries.
The problem is in the generated SQL code by EF:
exec sp_executesql N'
INSERT [dbo].[MyTable]([DateKey], [SomeProperty]) VALUES (#0, #1)
SELECT [Id] FROM [dbo].[MyTable]
WHERE ##ROWCOUNT > 0 AND [DateKey] = #0',
N'#0 datetime2(7), #1varchar(max)',
#0='2014-03-26 08:58:07',
#1='abcdef'
The parameter for DateKey is declared as DATETIME2. I think, This should be DATE.
This select statement cannot return any row, if there is any time part. If I change parameter #0 to DATE (as it is annotated in my model, I get no error even if time part is still included.
Is this a bug in EF?
Regards,
Daniel
EDIT:
I don't know if this is relevant for my question. I'm configured in OnModelCreating all my date column as DATETIME2 by default:
modelBuilder.Properties().Configure(p => p.HasColumnType("datetime2"));
EDIT on 2014-03-27
Providing a complete repro:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EFDateColumnAsKey
{
class Program
{
static void Main(string[] args)
{
MyContext ctx = new MyContext();
ctx.CalendarItems.Add(new CalendarItem() { StartDate = DateTime.Now.Date }); // This works
ctx.SaveChanges();
ctx.CalendarItems.Add(new CalendarItem() { StartDate = DateTime.Now }); // This not !!
ctx.SaveChanges();
ctx.Dispose();
}
public class MyContext : DbContext
{
public DbSet<CalendarItem> CalendarItems { get;set; }
}
public class CalendarItem
{
[DatabaseGenerated( System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Key, Column(Order = 1), DataType(DataType.Date)]
public DateTime StartDate { get; set; }
}
}
}
I have added some details to bug #2185. In summary, when we create a SqlCommand for the INSERT operation we define a parameter of type DATETIME2 for any property of type System.DateTime, but for compatibility reasons when we create the database schema by default we will create a column of type DATETIME.
The SQL we generate for the insert looks like this:
INSERT [dbo].[CalendarItems]([StartDate]) VALUES (#0);
SELECT [Id] FROM [dbo].[CalendarItems] WHERE ##ROWCOUNT > 0 AND [StartDate] = #0;
When you pass an arbitrary System.DataTime instance as a DATETIME2 parameter the least significant digits will be preserved, but as soon as the value of the DATETIME2 parameter gets stored in a DATETIME column, the value will be truncated.
Since the value stored in StartDate no longer matches the value of parameter #0, the second statement (i.e. the SELECT query we use to detect rows affected) will return zero rows and the command will fail, the transaction will get rolled back, etc.
As you already found, the possible workarounds are:
Use a surrogate identity key instead of declaring a key of type DateTime
Use DateTime2 for the column
Truncate the value in-memory before sending it to the database.
Hope this helps,
Diego