EF Core - Change column type from varchar to uuid in PostgreSQL 13: column cannot be cast automatically to type uuid - postgresql

Before:
public class MyEntity
{
public string Id { get; set; }
//...
}
Config:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
//...
modelBuilder.Entity<MyEntity>()
.Property(e => e.Id)
.ValueGeneratedOnAdd();
}
This was the previous developer's code which resulted in GUID values for the column. But in C# I had to deal with strings, so I decided to change the model.
After:
public class MyEntity
{
public Guid Id { get; set; }
//...
}
And I removed the ValueGeneratedOnAdd() code from Fluent API config.
I get the column "Id" cannot be cast automatically to type uuid error.
I think the key in this message is the automatically word.
Now my question is that since the values on that column are already GUID/UUID, is there any way to tell Postgres to change the varchar type to uuid and cast the current string value to UUID and put it in the column? I'm guessing there should be a SQL script that can do this without any data loss.

Use USING _columnname::uuid. Here is an illustration.
-- Prepare a test case:
create table delme (x varchar);
insert into delme (x) values
('b575ec3a-2776-11eb-adc1-0242ac120002'),
('4d5c5440-2776-11eb-adc1-0242ac120002'),
('b575f25c-2776-11eb-adc1-0242ac120002');
-- Here is the conversion that you need:
ALTER TABLE delme ALTER COLUMN x TYPE uuid USING x::uuid;
In your particular case:
ALTER TABLE "MyEntity" ALTER COLUMN "Id" TYPE uuid USING "Id"::uuid;
Btw, is your application the sole owner of the database model? If not then changing an existing table is a bad idea.

Related

EF Code First Model with Properties that Hold Encrypted Data

I've got some encrypted code in a table in the database which I am maintaining. This is circa 2012, so no "Always On" encryption. 3 columns contain encrypted data.
If I reverse engineer an EF domain, the Model which is created for that table contains properties for those columns which have a type byte[]. This is to be expected, as the columns are varbinary. So, it looks like this:
class Person
{
public byte[] FirstName { get; set; } // FirstName
}
Is there an elegant way to do some kind of EF mapping/configuration such that the FirstName class has a type of string and that it decrypts automagically by the framework? I realize I can just instantiate a Person object using sql, but it would be nice to offload this processing to the framework.
I've seen one of two solutions around where people are basically using a sql query for every property. They decorate the property with an Encrypt attribute and iterate the properties of every property. But with a sql query for every property for every object in a list - that does not exactly scale.
Has anyone "solved" this issue before?
Note: to retrieve the data, you first need to send a sql statement akin to:
OPEN SYMMETRIC KEY SomeKey DECRYPTION BY CERTIFICATE SomeCertificate
Thanks
In this answer I'm going to set out the things you need to do to deal with encrypted columns in EF. So, the columns in question will have a type of VARBINARY(MAX). Lets say you table looks something like this:
CREATE TABLE dbo.Person
(
SomeId int NOT NULL,
CreatedByUserId uniqueidentifier NULL,
CreatedUtcDate datetimeoffset(7) NULL,
Rowversion timestamp NULL,
FirstName varbinary(MAX) NULL,
LastName varbinary(MAX) NULL
)
Step 1 - Create a View which returns the decrypted columns. The view should basically be identical to your table, but for the columns which hold encrypted data, it should return the decrypted data. It would looks something like this:
CREATE VIEW [dbo].[v_Person]
AS
SELECT [SomeId]
,[CreatedByUserId]
,[CreatedUtcDate]
,[RowVersion]
,CONVERT(NVARCHAR(50),DECRYPTBYKEY([FirstName])) [FirstName]
,CONVERT(NVARCHAR(50),DECRYPTBYKEY([LastName])) [LastName]
FROM [dbo].[Person]
Step 2 - Create your domain model Person class with string as the relevant property type, not byte[] (note the select statement in the View above where we have cast the decrypted columns to NVARCHAR).
public class Person
{
public int SomeId { get; set; }
public string FirstName { get; set; } // string, not binary
public string LastName { get; set; } // string, not binary
public Guid CreatedByUserId { get; set; }
public DateTime CreatedUtcDate { get; set; }
public int SomeForeignKeyId { get; set; }
}
Step 3 - We need to set up a mapping for that Domain class. (The solution I am setting out here is for EF6. I am aware that EF Core does not support separate mapping files yet, so this would need to be done in the OnModelCreating event of you DbContext). Create a mapping class for you domain object which looks like this:
public class PersonMap : EntityTypeConfiguration<Person>
{
public PersonConfiguration(string schema)
{
ToTable("v_Person", schema); // note we map to the View
HasKey(x => x.SomeId);
// ... other properties elided for brevity
Property(x => x.FirstName)
.HasColumnName(#"FirstName")
.HasColumnType("nvarchar")
.IsOptional()
.HasMaxLength(50);
Property(x => x.LastName)
.HasColumnName(#"LastName")
.HasColumnType("nvarchar")
.IsOptional()
.HasMaxLength(50);
// Foreign keys
HasRequired(a => a.LogbookEntry)
.WithOptional(b => b.Person)
.WillCascadeOnDelete(false);
MapToStoredProcedures(p =>
p.Insert(i => i.HasName("Insert_Person"))
.Update(u => u.HasName("Update_Person"))
.Delete(d => d.HasName("Delete_Person")));
}
}
Note how we mapped to the view, v_Person, and not the raw table.
Also note the call to MapToStoredProcedures, which I explain next.
Step 4 - The last step is to create some stored procedures for your Insert, Update and Deletes. When you invoke SaveChanges, these will be invoked by EF and the relevant stored proc will be invoked depending on which EntityState the entity has. I won't set out all 3, but an example of the Update stored proc might look something like:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[Update_Person]
#SomeId INT,
#CreatedByUserId UNIQUEIDENTIFIER,
#CreatedUtcDate DATETIME,
#RowVersion_Original timestamp,
#FirstName NVARCHAR(50),
#LastName NVARCHAR(50) = NULL
AS
BEGIN
SET NOCOUNT ON;
DECLARE #CertKey NVARCHAR(7) = 'CertKey';
UPDATE PersonDetail
SET
FirstName = ENCRYPTBYKEY(KEY_GUID(#CertKey), #FirstName),
LastName = ENCRYPTBYKEY(KEY_GUID(#CertKey), #LastName)
WHERE SomeId = #SomeId
SELECT SomeId, RowVersion
FROM PersonDetail
WHERE SomeId = #SomeId
END
Feel free to comment if you have done it a better way.
Cheers

EF Core set Id to Int.MinValue and try to insert in database

I am using EF Core and I have a problem when I save a new entity.
Here is my model class
[Column("Id")]
public int ID { get; set; }
[Required]
[Column("Pratica", TypeName = "varchar(10)")]
public string PRATICA { get; set; }
[Column("Anno")]
public int ANNO { get; set; }
[Required]
[Column("Variante", TypeName = "varchar(2)")]
public string VARIANTE { get; set; }
Here I create and initialize a new PRAT object:
var prat = new PRAT();
prat.PRATICA = "Prova";
prat.ANNO = 2000;
prat.VARIANTE = "0";
context.PRAT.Add(prat);
context.SaveChangesAsync();
Just after the context.PRAT.Add(prat) line if I check prat.ID member I get something like -2147482647
After context.SaveChangesAsync I get the error "Cannot insert explicit value for identity column in table 'Prat' when IDENTITY_INSERT is set to OFF"
This is the generated SQL statement:
INSERT INTO [Prat] ([Id], [Anno], [Pratica], [Variante]) VALUES (#p0, #p1, #p2, #p3);
As you can see the Id Field is added to the list of fields, but this field is Identity!
If, before context.SaveChangesAsync() I set
prat.ID = 0
the generated SQL Statement is
INSERT INTO [Prat] ([Anno], [Pratica], [Variante]) VALUES (#p0, #p1, #p2);
And all works fine.
Thank you.
I think you need to configure your model with the DatabaseGenerated attribute, or configure it with fluent api
...
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Column("Id")]
public int ID { get; set; }
...
The primary key property is of type int, by convention EF Core assumes that the database will use the SQL
IDENTITY command to create a unique key when a new row is added. So you must define your database column as identity column.
For anyone still dealing with this, the other answers are insufficient. Primary keys for ints, shorts, guids etc in EF core are automatically generated.
The DatabaseGeneratedOption.Identity is for columns that are not primary keys.
The real problem is that somewhere in your code (potentially your database seeder if you have one) is pushing entities with manually entered primary keys.
For example:
_context.Jobs.Add(
new Job()
{
JobId = 1,
Name = "Truck Driver",
},
);
_context.SaveChanges();
Doing so tells ef core that you will be supplying primary keys for that entity and it will not know how to generate them. I am unsure why this is because you would think ef core could just grab the max value primary key and add 1 but I think the PK value generation code under the hood is the same for all primary key datatypes (including guid where max value isn't a thing).
Anyways, remove the code where you are manually inserting primary keys and the Add functionality should work as expected.

Cannot insert the value NULL into column 'EntryDate', table 'eApps.Logs.dbo.Logs'; column does not allow nulls. INSERT fails

I'm getting this error:
Cannot insert the value NULL into column 'EntryDate', table 'eApps.Logs.dbo.Logs'; column does not allow nulls. INSERT fails.
I have a standard add function to EF 6
public Logs AddLog(Logs p)
{
Logs.Add(p);
SaveChanges();
return p;
}
I've defined the field in my class as:
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime EntryDate { get; set; }
Should I be populating this field in my controller at all?
Please note that column EntryDate in table does not allow NULL. Hence, you must ensure that object p got valid date. Other option could be to change table schema and modify EntryDate column to accept NULL.
I added a constraint on the EntryDate field in SQL Server to default this field to GETDATE()

Entity Framework 5 recursive relations

CREATE TABLE ConfigurationItem
(
OID BIGINT NOT NULL
,ParentItemOID BIGINT
);
ALTER TABLE ConfigurationItem ADD CONSTRAINT PK_CONFIGURATIONITEM PRIMARY KEY (OID);
ALTER TABLE ConfigurationItem ADD CONSTRAINT FK_CONFIGURATIONITEM_PARENTITEMOID FOREIGN KEY (ParentItemOID ) REFERENCES CONFIGURATIONITEM(OID);
Every time fetch data ConfigurationItem I would like to get
parent ConfigurationItem
and List of child ConfigurationItems
and no recursion.
This was the entity created
[Table("ConfigurationItem", Schema = "dbo")]
public partial class ConfigurationItem : TaggableItem
{
public Int64 OID { get; set; }
public Int64? ParentItemOID { get; set; }
[ForeignKey("ParentItemOID")]
public ConfigurationItem Parent;
[InverseProperty("ParentItemOID")]
//Not a virtual because it is need to be marshalled via WCF
public List<ConfigurationItem> Children { get; set; }
}
I can't make this to work.
Example following errors happen:
InnerException: System.Data.SqlClient.SqlException
HResult=-2146232060
Message=Invalid column name 'ConfigurationItem_OID'.
Source=.Net SqlClient Data Provider
ErrorCode=-2146232060
Class=16
LineNumber=32
Number=207
Procedure=""
Server=localhost
State=1
What would be the correct way to make this work in Entity framework?
I think your original exception stems from the default naming convention used by EF. Either rename the OID property in the ConfigurationItem class to Configuration_OID or use the Column annotation on the OID property to indicate that you want to override the default naming convention for that column, e.g. [Column("OID")].
I have a schema with a self-referential table like this and I haven't found it necessary to use the InverseProperty annotation, so you may be able to just get rid of it.

EF 4.1 Code First. Table-per-type inheritance with different primary key name from its base class' primary key name

Given this:
create table Location(
LocationId int identity(1,1) not null primary key,
Address nvarchar(max) not null,
City nvarchar(max) null,
State nvarchar(max) not null,
ZipCode nvarchar(max) not null
);
create table Park(
ParkId int not null primary key references Location(LocationId),
Name nvarchar(max) not null
);
I tried this mapping:
modelBuilder.Entity<Location>();
modelBuilder.Entity<Park>().ToTable("Park");
modelBuilder.Entity<Park>().Property(x => x.LocationId).HasColumnName("ParkId");
Unfortunately that didn't work.
using (var db = new Ef())
{
var park = new Park { Name = "11th Street Park", Address = "801 11th Street", City = "Aledo", State = "TX", ZipCode = "76106" };
db.Set<Location>().Add(park);
db.SaveChanges();
}
It has this error:
The property 'LocationId' is not a declared property on type 'Park'.
Verify that the property has not been explicitly excluded from the
model by using the Ignore method or NotMappedAttribute data
annotation. Make sure that it is a valid primitive property.
How should I map Park entity so its LocationId property fall to ParkId column?
I have this mapping by the way:
public class Location
{
public virtual int LocationId { get; set; }
public virtual string Address { get; set; }
public virtual string City { get; set; }
public virtual string State { get; set; }
public virtual string ZipCode { get; set; }
}
public class Park : Location
{
public virtual string Name { get; set; }
}
If it could help, this is possible in EF 4.0 (via designer), just followed the steps in Chapter 2-11 of Entity Framework 4.0 Recipes, Problem Solution Approach. Now I'm trying it on code first via EF 4.1
[EDIT]
If I change the ParkId to LocationId, things are ok. However, with designer approach, it is possible to map the LocationId to ParkId of table Park; I want to achieve the same thing with code first
create table Park(
LocationId int not null primary key references Location(LocationId),
Name nvarchar(max) not null
);
As I know (and I tried it multiple times) code first doesn't support this => your derived type should use same column names for primary key.
This problem can be described very simply: Current fluent mapping implementation doesn't allow overriding mapping rules from parent entity => parent entity defines names of primary key columns in all derived entities.
IMO the most probable reason is that it was really designed as code first where you don't have existing database and you do not have to bother with database naming - it was up to EF to define names as it needed. Once DbContext API was released people started to use it with existing database massively. But here comes a problem: Initial use cases didn't count with this so some scenarios which are pretty easily done in EDMX are not possible. This is one of them.
Here is a workaround for this issue:
Create a view for the derived table and map your entity class that view. Rename the key column in your view so that it matches the key column in the base table.
eg:
base table User (UserID, FirstName, LastName)
derived table Manager (ManagerID, DepartmentID)
Entity Framework fails to update Manager as the key column is different!
solution:
create view UserManager
as
select
ManagerID as UserID,
DepartmentID
from Manager
Then map the Manager class to the UserManager view, instead of to the Manager table.