EntityFramework 4.1 DbContext select adding CAST columns with additional characters - entity-framework

i'm doing a standard GetAll() from dbcontext:
DbContext.Set<T>()
however i'm getting a weird message from oracle:
{"ORA-00904: \"Extent1\".\"Sub_Object_ID\": invalid identifier"}
if i look at generated sql (by looking at the query variable), i see that a few variables are added at the end as CASTS
"Extent1"."SomeEntity_ID", <-- correct
"Extent1"."SomeEnttiy2_ID", <-- correct
"Extent1"."Sub_Object", <-- correct
CAST( "Extent1"."SomeEntity_ID1" AS number(10,0)) AS "C3", <-- "1" appended
CAST( "Extent1"."SomeEnttiy2_ID1" AS number(10,0)) AS "C4", <-- "1" appended
CAST( "Extent1"."Sub_Object_ID" AS number(10,0)) AS "C5", <-- "_ID" appended
...
FROM "dbo"."MyEntity" "Extent1"
all the properties were correctly identified in the main portion of the select. however in the CAST portion, property names were appended with digits and _ID.. this is causing the select to fail..
looking at my entity, i have the properties specified once.. in this format:
public Nullable<decimal> SomeEntity_ID { get; set; }
what's with the casts?

this was just a matter of configuring foreign keys. I still don't understand the intention of this default behavior (adding a set of select columns for every foreign with appended "1")..
but declaring foreign keys fixes it.
via fluent API:
modelBuilder.Entity<FirmPerson>()
.HasRequired(f => f.Firm)
.WithMany(p => p.FirmPerson)
.HasForeignKey(f => f.FirmID);
or via attribute:
public int FirmID { get; set; }
[ForeignKey("FirmID")]
public virtual Firm Foo { get; set; }

Related

Including read-only columns in an Entity Framework model

Suppose I have a .NET Entity Framework model class:
public class Foo
{
public int FooId { get; set; }
public string Description { get; set; }
public DateTime Created { get; set; }
public DateTime LastUpdated { get; set; }
}
The Created and LastUpdated columns in my SQL Server table, both of type DATETIME2, have a DEFAULT constraint (SYSUTCDATETIME()). An AFTER UPDATE trigger sets LastUpdated to SYSUTCDATETIME whenever the Description is changed.
In my code, when I'm reading from the Foo table, I want Created and LastUpdated included, because I want to use their values. But when I'm adding a row to the table, I don't want them included in the Add because I want SQL Server to use the default value I've configured it to use. I thought it would just have been a matter of having
Foo foo = new Foo
{
Description = "This is my latest foo."
}
but C# is giving the two date properties their own default value of 0001-01-01T00:00:00.000000, which isn't null, and this is what's getting recorded in the table.
Isn't there an attribute that tells the framework not to write a property back to the database? It isn't NotMapped because that would prevent the values from being read.
`Don't you hate when you find the answer right after posting your question?
[DatabaseGenerated(DatabaseGeneratedOption.Computed)] omits the property from inserts and updates.
[DatabaseGenerated(DatabaseGeneratedOption.Identity)] omits the property from inserts.
The latter will take care of EndDate, which I didn't illustrate in my post. I have the database set a default value of 9999-12-31T23:59:59 on insert, but my application will change its value later when Foo is meant to expire.
What kills me is they based the naming on specific use cases that wouldn't come to mind in a different scenario. They ought to have gone with [SkipOnInsert] and [SkipOnUpdate] (which could then be combined as needed).

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.

EF Code First migration maxLength being set to 4000 instead of max

tl;dr: I have a column that should be NVARCHAR(MAX) but scaffolding a migration with Add-Migration is giving me a column with max length 4000 in Up(). What do I have to do to get this to be MAX?
Consider the following model:
public class Foo
{
public int Id { get; set; }
[Required]
[StringLength(100)]
public string Name { get; set; }
[Required]
[DataType(DataType.MultilineText)]
public string Memo { get; set; }
[Required]
[DataType(DataType.EmailAddress)]
public string Email { get; set; }
}
I have two conventions set up in Context.OnModelCreating:
A convention which sets the default maxLength of any string property not otherwise configured to 1024, with the line:
Properties<string>().Configure(c => c.HasMaxLength(1024));
An attribute-based convention which reads the DataTypeAttribute and sets some properties accordingly, namely the column type and length:
switch (attribute.DataType)
{
case DataType.MultilineText:
configuration.HasColumnType("nvarchar").IsMaxLength();
break;
case DataType.EmailAddress:
configuration.HasColumnType("nvarchar").HasMaxLength(255);
break;
}
I tested this all before setting up migrations and it worked beautifully, giving me the following table - note that Memo is being created as NVARCHAR(MAX):
CREATE TABLE dbo.Foo (
Id INT IDENTITY NOT NULL,
Name NVARCHAR(100) NOT NULL,
Memo NVARCHAR(MAX) NOT NULL,
Email NVARCHAR(255) NOT NULL,
CONSTRAINT [PK_Foo] PRIMARY KEY (Id)
)
Then I enabled migrations and added one, and got the following table definition in Up():
CreateTable(
"dbo.Foo",
c => new
{
Id = c.Int(nullable: false, identity: true),
Name = c.String(nullable: false, maxLength: 100),
Memo = c.String(nullable: false, maxLength: 4000),
Email = c.String(nullable: false, maxLength: 255),
})
.PrimaryKey(t => t.Id)
Wha? Where'd that maxLength: 4000 come from? It's not the MAX I would expect, nor is it even the 1024 I configured as the default with my first convention. It's certainly respecting the 100 and 255 of Name and Email (proving my attribute-based convention is working)...
So, is this a bug in the framework, or is there something about migrations that I'm not understanding? And either way, what can I do to get Code First Migrations to respect my IsMaxLength()?
Apparently IsMaxLength() has a different meaning in the context vs in a migration. In the context, it creates a column of NVARCHAR(MAX) but once you enable migrations this is interpreted as "the biggest possible value the column allows, except for MAX". As #marc_s points out in a comment, this is 4000 for a nvarchar column.
Indeed, while I have read this works outside of migrations, setting HasMaxLength(int.MaxValue) and calling Add-Migration gives me the following error:
(0,0) : error 0026: MaxLength '2147483647' is not valid. Length must be between '1' and '4000' for 'nvarchar' type.
Playing around a little more, I decided to try HasColumnType("nvarchar(MAX)"), which to my surprise worked! So replacing the call in the convention above works.
I can't find any way to read out the result of IsMaxLength() anywhere where I can set the type (for example IStoreModelConvention is too late), so it's not the prettiest solution, but at least I can move forward.

How do I access a table that has a composite key I can't model using Code First?

I have an existing database that I cannot change, but want to access using EF. For 90% of the database, I have been able to get Code First EF to work, and I am very impressed.
I've run into a case that I am wondering how to model or access the data through a navigation property.
In one case, the tables are like this (this example is totally made up, but represents the problem):
CREATE TABLE Dog (
id INTEGER PRIMARY KEY,
name VARCHAR(50) NULL,
breed_id INTEGER NOT NULL
);
CREATE TABLE Breed (
id INTEGER NOT NULL,
organization_id INTEGER NOT NULL,
description VARCHAR(100) NOT NULL,
Primary key (id, organization)
);
CREATE TABLE Organization (
id INTEGER PRIMARY KEY,
description VARCHAR(100) NOT NULL
);
In Table breed, the organization represents an organization that has defined a breed. A dog can have several breeds, but the program only displays one, the results being 'filtered' by the organization id - which is a value that is configured when the program is set up.
An example of the data that might be present is this:
id organizationID Description
1 1 Basset Hound
2 1 Great Dane
2 2 Grande Dane
Where organization 2 has chosen to call the breed something different than organization 1. The unique primary key is a combination of id and organizationID. A dog has a breed, but does not have a property to define one or more associated organizations. It takes additional information from another table, or a configured value (perhaps an enumerated value) to find the breed of a dog.
In my case, to find a particular dog breed, you have to have a dog id and another piece of information (organization_id) which is related to program configuration.
The dog, breed and organization classes look like this:
public class Dog {
public int id { get; set; };
public string name { get; set; };
public int breedID { get; set; };
public virtual Breed { get; set; }
}
public class Breed {
public int id { get; set; };
public int organizationID { get; set; };
public string description { get; set; };
public virtual Organization { get; set; }
}
public class Organization {
public int id { get; set; };
public string description { get; set; };
}
As seen in the code, I'd like to use a "Navigation" Property on Dog that returns a breed, but don't think I can configure this in code first.
I've tried a few different things (in fluent API, and leaving organization out - since that's easy) and will also document things I don't think work:
1)
modelBuilder.Entity<Dog>().HasKey(t => t.id);
modelBuilder.Entity<Breed>().HasKey(t => t.id);
modelBuilder.Entity<Dog>().HasRequired(d => d.Breed).WithMany().HasForeignKey(d => d.breedID);
Of course, the problem with this is that more than one breed will be returned and entity framework will throw an exception because the breedID itself is insufficient to yield a single value - which the model is calling for.
2)
Change class dog:
Remove:
public virtual Breed { get; set; }
Add:
public virtual ICollection<Breed> Breeds { get; set; }
public Breed Breed {
get {
// Assume 1 is configured organization value
return Breeds.Single(t => t.OrganizationId == 1);
};
set {
Breeds.Add(value);
}
}
Change model:
I don't know how to do this for the given classes. Since it's a Collection, it must look something like
modelBuilder.Entity<Dog>().HasMany(d => d.Breeds)...
but I don't see how to specify that breedID is a foriegn key into the breed table.
If I could get the model to work, the rest will work, but it does seem wierd and inefficient.
3)
Change model to account for composite key (using first set of classes):
modelBuilder.Entity<Breed>().HasKey(b => new { b.id, b.organizationId });
modelBuilder.Entity<Dog>().HasRequired(t => t.Breed).WithMany().HasForeignKey(t => new { t.breedID, configuredValue });
I don't know how to "inject" configuredValue as in the last line, so this doesn't work either.
If none of the above methods work, or if I can't find another way to configure code first properly, then I'd like to specify that when the Breed navigation property getter is called, it should use a query that can get the appropriate breed and return it appropriately.
However, I don't want to dirty my POCO with the Context calls to return the result of the query. In other words, I'd like to have a property on Dog that does NOT look something like this:
public Breed Breed {
get {
return context.Breed.Where(b => b.id == this.id && b.organizationID == 1).Single();
}
}
Ideally, it would work like the Navigation collections do, where EF does it's magic and returns the appropriate results.
Intuitively, it seems like I should be able to either configure this using POCO-like code or use/extend a proxy to extend the configuration to use the particular query I want when the accessor is called. Or - it seems like I ought to be able to populate the property on any read and dirty the POCO on write. I'm just not familiar enough with EF to know how to do this.
Is this possible?
As an addition to the first post, because I desire to keep my POCO classes clean, I think I will probably implement the Repository pattern to encapsulate the complex queries like the ones I've described, as well as support other operations also.
Looking at the EDMX that I've generated from my Code First model, it's not apparent how to implement the model from a database first perspective, either.
Any thoughts are greatly appreciated.
In short, you can't do that. You have illogical database structure (foreign key referencing non-unique column), you'll have to map it somewhere. You can't do that in EF configuration, because it's dynamic, so you'll have to do that inside you entity classes. And I see no ways to do that without direct call to context inside your entity class (or helper class).