I am new to EF so here goes.I have a class which contains the following
public class EmailTemplate
{
public Guid Id { get; set; }
[MaxLength(2000)]
public string Html { get; set; }
}
Here is my mapping class
class EmailMapper : EntityTypeConfiguration<EmailTemplate>
{
public EmailMapper()
{
ToTable("EmailTemplate");
HasKey(c => c.Id);
Property(c => c.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
Property(c => c.Id).IsRequired();
}
}
I am trying to call DbContext.SaveChanges(), but I get the following error :
Exception Details: System.Data.SqlClient.SqlException: Cannot insert the value NULL into column 'Id', table 'AutoSendConnection.dbo.EmailTemplates'; column does not allow nulls. INSERT fails.
What am i doing wrong? Why won't EF auto create a unique GUID?
Just decorate the Id field on your EmailTemplate class as below and SQL Server will automatically generate the value on insert.
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public Guid Id { get; set; }
You can also remove your Mapper class as it's no longer needed.
If using .Net core then this should work for you ...
Use fluent API
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Node>().Property(x => x.ID).HasDefaultValueSql("NEWID()");
}
or
modelBuilder.Entity<Student>().Property(p => p.StudentID)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
Here is a more comprehensive Cheat Sheet for entity framework
Addressing other answers here
None of these other options seem to work and I've questioned this time and time again with the EF team over on github ...
https://github.com/aspnet/EntityFramework6/issues/762
... for some reason the EF dev team seem to think that this is "working by design" and repeatedly close tickets questioning this "bug".
The EF team explanation
For some reason they seem to think that "generating Guids in SQL is considered not best practice and that to ensure the keys are available immediately we should be generating the keys in the app code".
The issue here of course is that highly populated tables run the risk of you taking further business actions consuming an invalid key.
In my case this could break some extremely complex multi server DTC transactions so I don't believe the advice from MS to be correct, that said EF Core doesn't currently support distributed transactions at all so in a focused context they may have a point.
My answer (which actually works)
In short, I solved this by "manually hacking" the generated migration after generating it ...
EF code first migrations, DB generated guid keys
To quote the other question the answer is as follows:
Generate the migration script as you normally would putting both attributes on the key property like this ...
public class Foo
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
}
... declaratively speaking the entity is now correct.
The migration it will generate will look something like:
CreateTable(
"dbo.Foos",
c => new
{
Id = c.Guid(nullable: false),
...
})
.PrimaryKey(t => t.Id)
...;
... I have not been able to pin down why, but in some situations this will work and in others it won't (run the migration, perform an insert to test).
If it fails, roll the migration back then modify it to read something like ...
CreateTable(
"dbo.Foos",
c => new
{
Id = c.Guid(nullable: false, defaultValueSql: "newid()"),
...
})
.PrimaryKey(t => t.Id)
...;
... the extra code here tells SQL to generate the key as we would expect.
As a rule of thumb I would apply this change all the time for consistency reasons and it means that at a glance your migrations will show you exactly what keys are db generated and of course which ones don't.
After a long investigation, I found out that in EF Core 3.1 you need to use
builder.Property(e => e.Id).ValueGeneratedOnAdd();
Set the default sql value of the field to 'newsequentialid()' in the mapping configuration.
You can also set Default Value of ID as NewID() in Sql Server itself and pass the GUID as null
I used to do it in SSMS.
I prefer to leave the database to generate the id automatically for example the following schema:
CREATE TABLE [dbo].[MyTable](
[MyId] [uniqueidentifier] NOT NULL CONSTRAINT [DF_Booking_BookingId] DEFAULT (newsequentialid())
)
Then in the code first mapping I specify the following to tell Entity Framework that the database will take care of generating the value on insert.
Property(a => a.MyId).IsRequired().HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
I encountered this issue in .NET 6. Some background is that we use int for primary keys but a new requirement ment that parts of the system needed to be in sync with other systems and we could no longer depend on our int keys. We decided to use a hybrid approach with auto-increment primary key integer id column and a GUID column as described here:
https://dba.stackexchange.com/a/96990/80960
Basically the model looked like this:
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Guid { get; set; }
Migration looked like this:
migrationBuilder.AddColumn<Guid>(
name: "Guid",
table: "Products",
type: "uniqueidentifier",
nullable: false,
defaultValue: new Guid("00000000-0000-0000-0000-000000000000"));
ApplicationDbContextModelSnapshot.cs:
b.Property<Guid>("Guid")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
I thought this meant that every old value would get 00000000-0000-0000-0000-000000000000 and new values would receive ValueGeneratedOnAdd. However when I tested to add a new value I still got value 00000000-0000-0000-0000-000000000000. See image below:
I then tried to use defaultValue: Guid.NewGuid()); instead of defaultValue: new Guid("00000000-0000-0000-0000-000000000000")); in migration.
This meant that every old and every new value I added got the same Guid but not 00000000-0000-0000-0000-000000000000.
I then switched over to manually add defaultValueSql: "NEWID()" instead of defaultValue: new Guid("00000000-0000-0000-0000-000000000000") in migration.
Now everything started working as expected both with old values and new values:
Now I wanted this functionality to be default for every new Guid added. I therefore modified ApplicationDbContext.cs method protected override void OnModelCreating(ModelBuilder modelBuilder) like this:
foreach (var property in modelBuilder.Model.GetEntityTypes()
.SelectMany(t => t.GetProperties())
.Where(p => p.ClrType == typeof(Guid)))
{
property.SetDefaultValueSql("NEWID()");
}
Model ended up looking like this since [DatabaseGenerated(DatabaseGeneratedOption.Identity)] did not work in this case:
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public Guid Guid { get; set; }
Entity Framework Core Update:
There is no need to use [DatabaseGenerated(DatabaseGeneratedOption.Identity)].
There is no need to use fluent API
EF Core automatically take care of it and generates Id for primary key
Example:
public class DummyEntity
{
public Guid Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
Seeding
_context.DummyEntities.Add(new DummyEntity
{
FirstName = "Abc",
LastName = "Def",
Postion = "User",
});
_context.SaveChanges();
Related
I created a table via entity framework code-first with a primary key set to auto increment, but now I want to remove that auto-incrementing from the column. I've tried doing that with both fluent API:
public class ProductTypeMap: EntityTypeConfiguration<ProductType>
{
public ProductTypeMap()
{
// This is an enum effectively, so we need fixed IDs
Property(x => x.ProductTypeId)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
}
}
And an annotation:
public class ProductType
{
[Required, Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public int ProductTypeId { get; set; }
public string ProductTypeName { get; set; }
}
And in both cases they produce the same migration code:
public partial class removeproducttypeidentity : DbMigration
{
public override void Up()
{
DropPrimaryKey("dbo.ProductTypes");
AlterColumn("dbo.ProductTypes", "ProductTypeId", c => c.Int(nullable: false));
AddPrimaryKey("dbo.ProductTypes", "ProductTypeId");
}
public override void Down()
{
DropPrimaryKey("dbo.ProductTypes");
AlterColumn("dbo.ProductTypes", "ProductTypeId", c => c.Int(nullable: false, identity: true));
AddPrimaryKey("dbo.ProductTypes", "ProductTypeId");
}
}
However, when I run that migration on the database, the Identity Specification is not removed from the SQL Server 2008 database table?
I also tried explicitly turning off the Identity in the migration as follows, but that didn't do it either:
AlterColumn("dbo.ProductTypes", "ProductTypeId", c => c.Int(nullable: false, identity: false));
Is there another way to tell SQL to remove the Identity?
As others have said, EF can't do this natively, although it will create a migration script that makes it look like it has. I've experimented with lots of ways of doing this and I find the easiest way is to:
Back up the database...
Change your class so it no longer has an identity in code (by removing the attribute or mapping)
Generate the migration with the Package Manager Console (add-migration [your migration name] )
Comment out the code in Up method in the newly generated migration
Add a new line of code ready to receive the SQL you'll generate below: Sql (#" ");
Go into SSMS and make sure it's set to generate scripts when you make a table change
Remove the identity in the table designer in SMSS
Save the table change in SSMS and copy the SQL generated. (That SQL makes a copy of the table with the data, drops the original table, then recreates the original table without the identity set on the column, then copies all the data back and adds all the foreign keys and constraints back on again)
Paste the SQL you just copied between the speech marks in the code you added above.
Run the migration
That should then give you a migration that removes the identity and can be run on other copies of the database successfully.
NB:The Down method that was generated won't work either because it will be adding the identity back on, which EF also can't do. If you need the Down method to work create a copy of the SQL you added and adjust it to create the table with the identity again.
The approach above is a quick way of what #Georg Patscheider describes so well in his answer.
If you want to remove identity, you need to
Step 1. And an annotation to the key column
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public short Id { get; set; }
this will generate the migration script
AlterColumn("dbo.TableName", "Id", c => c.Long(nullable: false));
But this script won't remove identity from the key column.
Step 2. manually remove identity from the key column in SQL Management Studio.
You don't need to back up the database.
If you get an error, you might need to go to "Tools"->"Options"->"Designers", and uncheck the "Prevent saving changes that require table re-creation"
You can not use ALTER COLUMN to set whether a column is an identity column (How to alter column to identity(1,1)).
Instead, you have to:
(backup DB)
CREATE TMP_table with columns of original table, but ID column set to identity: false
SET IDENTITY_INSERT [TMP_Table] ON
copy data from original to TMP table
SET IDENTITY_INSERT [TMP_Table] OFF
DROP original table
Rename TMP_table to original table name (EXECUTE sp_rename)
Tip: change the column in SQL Management Studio and inspect the emitted script (SQL SERVER – Add or Remove Identity Property on Column).
If you want to remove the Identity from a Primary Key, you’re going to need to Drop and Recreate the table and any associated Foreign Keys along with it.
Removing an identity specification from a column can only be done by re-creating the table. Look at the ALTER TABLE - ALTER COLUMN statement: there is no syntax to change (add or remove) an identity specification.
Refer flowing sites:
Cant remove identity attribute from PK
Entering keys manually with Entity Framework
EF6: Switching Identity On/Off with a Custom Migration Operation
Remove Identity from Primary Key with Drop and Recreate Table
Thanks to both of the answers explaining about what's involved in dropping the identity from the column I came up with a new version of the migration that achieves the removal of the identity from the PK column.
This solution loses all of the data in the table. This is acceptable in my case because the table in question is one that represents an enum and so is populated manually by my seed method (I was removing the identity in the first place so I could dictate the values used for the PK Id fields). Do not use this approach for user entered data!
public partial class removeproducttypeidentity : DbMigration
{
public override void Up()
{
DropForeignKey("dbo.Products", "ProductTypeId", "dbo.ProductTypes");
DropPrimaryKey("dbo.ProductTypes");
DropTable("dbo.ProductTypes");
CreateTable(
"dbo.ProductTypes",
c => new
{
ProductTypeId = c.Int(nullable: false),
ProductTypeName = c.String(),
})
.PrimaryKey(t => t.ProductTypeId);
AddForeignKey("dbo.Products", "ProductTypeId", "dbo.ProductTypes", "ProductTypeId");
}
public override void Down()
{
DropForeignKey("dbo.Products", "ProductTypeId", "dbo.ProductTypes");
DropPrimaryKey("dbo.ProductTypes");
DropTable("dbo.ProductTypes");
CreateTable(
"dbo.ProductTypes",
c => new
{
ProductTypeId = c.Int(nullable: false, identity: true),
ProductTypeName = c.String(),
})
.PrimaryKey(t => t.ProductTypeId);
AddForeignKey("dbo.Products", "ProductTypeId", "dbo.ProductTypes", "ProductTypeId");
}
}
This solution works well for when:
you can't lose data
recreating the table would take too long (e.g. you have millions of rows and copying would take a long time)
you want to handle everything with EF rather than writing custom migrations (the approach from #Georg Patscheider and #tomRedox go around EF)
which is a good idea because future migrations written by EF are less likely to have issues
(in my database, I have hundreds of migrations, some with custom SQL, which is causing me trouble years after those migrations were initially written because EF doesn't know about them!)
The solution
Create a new column and copy all the old IDs to it
Make the new column the primary key
Update all references to the old column to the new column
The setup
Example entities:
public class Foo
{
[Key]
public int Id { get; set; }
}
public class Bar
{
// This is the foreign key:
public int FooId { get; set; }
public virtual Foo Foo { get; set; }
}
Add a new property which has identity set to false
Change Foo to:
public class Foo
{
[Key]
public int Id { get; set; }
[Index(IsUnique = true)]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int NewId { get; set; }
}
Generate a migration:
public partial class AddNewId : DbMigration
{
public override void Up()
{
AddColumn("dbo.Foos", "NewId", c => c.Int(nullable: false));
// This is a custom line which copies the data from the old column to the new.
Sql("UPDATE dbo.Foos SET NewId = Id");
CreateIndex("dbo.Foos", "NewId", unique: true);
}
public override void Down()
{
DropIndex("dbo.Foos", new[] { "NewId" });
DropColumn("dbo.Foos", "NewId");
}
}
Change references from Foo.Id to Foo.NewId
Here's the tricky part.
Any references to the Foo entity are still using the old Id property rather than the new NewId property.
Using Visual Studio:
Temporarily comment out the old Foo.Id property
Manually change the name of Foo.NumericId to Foo.Id (without updating references)
Rename Foo.Id back to Foo.NumericId but have VS also change all references
Comment back in the old Foo.Id property that was commented out in step 1
Make Foo.NewId the primary key
public class Foo
{
public int Id { get; set; }
[Key]
[Index(IsUnique = true)]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int NewId { get; set; }
}
Update Bar.FooId
All entities now need to reference Foo.NewId instead of Foo.Id.
Change Bar:
public class Bar
{
// This is the foreign key:
public int FoodNewId { get; set; }
public virtual Foo Foo { get; set; }
}
Create a new migration:
public partial class AddNewIdPart2 : DbMigration
{
public override void Up()
{
DropForeignKey("dbo.Bars", "FooId", "dbo.Foos");
RenameColumn(table: "dbo.Bars", name: "FooId", newName: "FooNewId");
DropPrimaryKey("dbo.Foos");
AddPrimaryKey("dbo.Foos", "NewId");
AddForeignKey("dbo.Bars", "FooNewId", "dbo.Foos", "Newid");
}
public override void Down()
{
// ...
}
}
Done!
That should be everything.
You can now remove the old Foo.Id column if you want.
I have a simple code in Entity Framework (EF) v4.1 code first:
PasmISOContext db = new PasmISOContext();
var user = new User();
user.CreationDate = DateTime.Now;
user.LastActivityDate = DateTime.Now;
user.LastLoginDate = DateTime.Now;
db.Users.Add(user);
db.SaveChanges();
user.Avatar = new Avatar() { Link = new Uri("http://myUrl/%2E%2E/%2E%2E") };
db.SaveChanges();
db.Users.Add(new User() { Avatar = new Avatar() { Link = new Uri("http://myUrl/%2E%2E/%2E%2E") } });
db.SaveChanges();
The problem is that I get an error
An error occurred while saving entities that do not expose foreign key
properties for their relationships. The EntityEntries property will
return null because a single entity cannot be identified as the source
of the exception. Handling of exceptions while saving can be made
easier by exposing foreign key properties in your entity types. See
the InnerException for details.
at
db.Users.Add(new User() { Avatar = new Avatar() { Link = new Uri("http://myUrl/%2E%2E/%2E%2E") } });
db.SaveChanges();
I don't understand why the similar operation works. Is there something wrong with my model, or with ef-code-first?
public class Avatar
{
[Key]
public int Id { get; set; }
[Required]
public string LinkInString { get; set; }
[NotMapped]
public Uri Link
{
get { return new Uri(LinkInString); }
set { LinkInString = value.AbsoluteUri; }
}
}
public class User
{
[Key]
public int Id { get; set; }
public string UserName { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public Avatar Avatar { get; set; }
public virtual ICollection<Question> Questions { get; set; }
public virtual ICollection<Achievement> Achievements { get; set; }
public DateTime CreationDate { get; set; }
public DateTime LastLoginDate { get; set; }
public DateTime LastActivityDate { get; set; }
}
For those of you who would still have this error with all keys properly defined, have a look at your entities and make sure you don't leave a datetime field with a null value.
This error message can be thrown for any kind of reason. The 'InnerException' property (or its InnerException, or the InnerException of that, etc) contains the actual primary cause of the problem.
It would of course be useful to know something about where the problem occurred - which object(s) in the unit of work is causing the problem? The exception message would normally tell you in the 'EntityEntries' property, but in this case, for some reason, that can't be done. This diagnostic complication - of the 'EntityEntries' property being empty - is apparently because some Entities 'do not expose foreign key properties for their relationships.'
Even if the OP gets the error because of failing to initialize DateTimes for the second instance of User, they get the diagnostic complication - 'EntityEntries' being empty, and a confusing top-level message ... because one of their Entity's doesn't 'expose foreign key properties'. To fix this, Avatar should have a public virtual ICollection<User> Users { get; set; } property defined.
The issue was resolved by adding an FK property.
In my case the following situation was giving me the same Exception:
Imagine a code first EF model where you have a Garage entity that has a collection of Car entities. I needed to remove a car from the garage so I ended up with code that looked like this:
garageEntity.Cars.Remove(carEntity);
Instead, it should've been looked like this:
context.Cars.Remove(carEntity);
Just for others who might have similar problems. I had the same error, but for a different reason. In one of the child objects I defined the [Key] as being a value which was the same for different saves. A stupid mistake on my part, but the error message does not instantly lead you to the problem.
In my case the exeception was thrown because EF had created a migration incorrectly.
It missed setting the identity: true on the second table. So go into the migrations which created the relevant tables and check if it missed to add identity.
CreateTable(
"dbo.LogEmailAddressStats",
c => new
{
Id = c.Int(nullable: false, identity: true),
EmailAddress = c.String(),
})
.PrimaryKey(t => t.Id);
CreateTable(
"dbo.LogEmailAddressStatsFails",
c => new
{
Id = c.Int(nullable: false), // EF missed to set identity: true!!
Timestamp = c.DateTime(nullable: false),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.LogEmailAddressStats", t => t.Id)
.Index(t => t.Id);
An Id column should have identity (i.e. auto-incrementing!) so this must be a EF bug.
You could add identity manually with SQL directly to the database but I prefer using Entity Framework.
If you run in to the same problem I see two easy solutions:
Alt 1
reverse the incorrectly created migration with
update-database -target:{insert the name of the previous migration}
Then add the identity: true manually to the migration code and then update-database again.
Alt 2
you create a new migration that adds identity. If you have no changes in the models and you run
add-migration identity_fix
it will create an empty migration. Then just add this
public partial class identity_fix : DbMigration
{
public override void Up()
{
AlterColumn("dbo.LogEmailAddressStatsFails", "Id", c => c.Int(nullable: false, identity: true));
}
public override void Down()
{
AlterColumn("dbo.LogEmailAddressStatsFails", "Id", c => c.Int(nullable: false));
}
}
This problem can also arise from reversed key declarations. If you're using fluent to configure the relationship, make sure the left and right keys are mapped to the correct entity.
I hade same probleme. in my case, it was due to datetime field with a null value. I had to passe a value to datetime and evrythings went fine
Another answer:
I used this:
public List<EdiSegment> EdiSegments { get; set; }
instead of this:
public virtual ICollection<EdiSegment> EdiSegments { get; set; }
and got the error message noted above.
I had the same error and in my case the problem was that I added a relationship object which had already been loaded "AsNoTracking". I had to reload the relation property.
BTW, Some suggest using "Attach" for relations that already exist in db, I haven't tried that option though.
In my case, the problem was that I renamed a column improperly, so the migration made two columns, one called "TeamId" and one called "TeamID". C# cares, SQL doesn't.
Yet another different case here.
A query was cast to a list and while doing that, it created entities by their constructor for comparison in the linq expression right after the ToList(). This created entities that gotten into the deleted state after the linq expression finished.
However! There was a small adjustment that created another entity in the constructor, so that this new entity got linked to an entity that was marked as Deleted.
Some code to illustrate:
query.Except(_context.MyEntitySetSet()
.Include(b => b.SomeEntity)
.Where(p => Condition)
.ToList() // This right here calls the constructor for the remaining entities after the where
.Where(p => p.Collection.First(b => Condition).Value == 0)
.ToList();
The constructor of MyEntity:
public partial class MyEntity
{
protected MyEntity()
{
// This makes the entities connected though, this instance of MyEntity will be deleted afterwards, the instance of MyEntityResult will not.
MyEntityResult = new MyEntityResult(this);
}
}
My solution was to make sure the entire expression was done inside the IQueryable so that there won't be any objects created.
I'm not entirely sure that it's going to help in your case because I'm setting up my tables using Fluent API, however, as far I can tell, the issue arises regardless whether the schema is set up using data annotations (attributes) or Fluent API (configuration).
There seems to be a bug in EF (v. 6.1.3) as it omits certain changes to the schema when updating the DB to the next migration. The quickest route around it is (during the development stage) to remove all the tables from the DB and runt migrations from init stage again.
If you're already in production, the quickest solution I've found was to manually change the schema in the DB or, if you want to have version control of the changes, manually manipulate the methods Up() and Down() in your migration.
Today I faced this issue and tried the possible solutions posted above but none of them helped me. I had UnitOfWork pattern implemented and system was committing the data in last after adding all the records.
In my case system was combining the two models and querying the DB
Invalid object name 'dbo.RoleModelUserModel'.
where these were two different models actually.
I fixed this by reordering the insert statements and adding the parent entity first. In this case added the user first and issue resolved.
After a bit of investigation I found that whilst .Net supports a minimum date (DateTime.MinValue) of 01/01/0001 00:00:00 and a maximum (DateTime.MaxValue) of 31/12/9999 23:59:59 in SQL Server Compact Edition minimum date is 01/01/1753 00:00:00.
When I entered a date greater than 01/01/1753 00:00:00, this error disappeared.
Is your application or website being accessed from some third party application when this error is coming? If yes, then please check the access rights of the account which is sending the request to your application.
In our case, it was ServiceNow MID server service which was the culprit. It is a Windows service. If you want to know more about it then please read this link. So basically, you need to check two things:
Under the context of which account the calling service should run to access your application?
What all access rights are needed for the service's log on account to do all allowed operations in your application?
As per this article of ServiceNow we had to give Log on as a service right to the MID Server service's log on account. You can do it via in Local Security Policies console (Refer screenshot).
After we gave the proper access rights to the logon account, the Entity Framework issue went away. Please remember that the access rights and the log on account to be used will be specific to your application.
I have declared the following model using the EF Core fluent API:
modelBuilder.Entity<Foo>()
.HasKey(p => new { p.Name, p.Id });
Both fields are marked as the primary key when I create the database in PostgreSQL but the Id field is not marked as auto increment. I have also tried to add
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
to the Id field under Foo without it making any difference on the migration code. Is there a way to make the Id AI although it is a PPK?
Well those Data Annotations should do the trick, maybe is something related with the PostgreSQL Provider.
From EF Core documentation:
Depending on the database provider being used, values may be generated
client side by EF or in the database. If the value is generated by the
database, then EF may assign a temporary value when you add the entity
to the context. This temporary value will then be replaced by the
database generated value during SaveChanges.
You could also try with this Fluent Api configuration:
modelBuilder.Entity<Foo>()
.Property(f => f.Id)
.ValueGeneratedOnAdd();
But as I said earlier, I think this is something related with the DB provider. Try to add a new row to your DB and check later if was generated a value to the Id column.
First of all you should not merge the Fluent Api with the data annotation so I would suggest you to use one of the below:
make sure you have correclty set the keys
modelBuilder.Entity<Foo>()
.HasKey(p => new { p.Name, p.Id });
modelBuilder.Entity<Foo>().Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
OR you can achieve it using data annotation as well
public class Foo
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key, Column(Order = 0)]
public int Id { get; set; }
[Key, Column(Order = 1)]
public string Name{ get; set; }
}
To anyone who came across this question who are using SQL Server Database and still having an exception thrown even after adding the following annotation on the int primary key
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
Please check your SQL, make sure your the primary key has 'IDENTITY(startValue, increment)' next to it,
CREATE TABLE [dbo].[User]
(
[Id] INT IDENTITY(1,1) NOT NULL PRIMARY KEY
)
This will make the database increments the id every time a new row is added, with a starting value of 1 and increments of 1.
I accidentally overlooked that in my SQL which cost me an hour of my life,
so hopefully this helps someone!!!
Annotate the property like below
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
To use identity columns for all value-generated properties on a new model, simply place the following in your context's OnModelCreating():
builder.ForNpgsqlUseIdentityColumns();
This will create make all keys and other properties which have .ValueGeneratedOnAdd() have Identity by default. You can use ForNpgsqlUseIdentityAlwaysColumns() to have Identity always, and you can also specify identity on a property-by-property basis with UseNpgsqlIdentityColumn() and UseNpgsqlIdentityAlwaysColumn().
postgres efcore value generation
Specifying the column type as serial for PostgreSQL to generate the id.
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Column(Order=1, TypeName="serial")]
public int ID { get; set; }
https://www.postgresql.org/docs/current/static/datatype-numeric.html#DATATYPE-SERIAL
There are a few questions out there on this topic, but my question is very specific to true self referencing. All the examples for other questions are circular references and that doesn't help me in this case.
Lets say I have this model:
public class User
{
[Key]
public int Id { get; set; }
...
public int CreatedByUserId { get; set; }
}
and this map:
public class UserMap : EntityTypeConfiguration<User>
{
public UserMap()
{
this.HasRequired(a => a.CreatedByUser)
.WithMany()
.HasForeignKey(u => u.CreatedByUserId);
}
}
After migrations generates a database with this code I can manually add a User in SQL Management Studio with Id = 1, and CreatedByUserId = 1 so that tells me that self references like this can work.
However when using EF to create a user, I run into a "unable to determine a valid ordering for dependent operations" issue. There are a lot of questions on the matter that involve a new entity that refers another new entity that has a foreign key on the first entity, which is a circular reference. The solution in those cases is either save one of entities first or to have a nullable id on the circular entity foreign key. I can not do either of those because the first would be impossible and the second is a external constraint that I cannot have nullable ids.
So, seeing how I can achieve this by adding a entry manually I can assume it's a limitation of EF5. What are the work arounds?
You can still satisfy your interface and do the save first then set method by adding another property to act as a nullable backer for CreatedByUserId:
public class User : ICreatable
{
[Key]
public int Id { get; set; }
...
public int CreatedByUserId
{
get
{
if (!_CreatedByUserId.HasValue)
//throw new exception, something went wrong.
return _CreatedByUserId;
}
set
{
_CreatedByUserId = value;
}
}
int? _CreatedByUserId { get; set; }
}
You may want to rethink the possibility that a user can create him or herself...
However if you really want to do this then there is a solution. Your main problem is the fact that your column is an IDENTITY column which means that EF doesn't specify the Id, SQL server is giving each row an auto-incrementing Id. Any value you set as the Id is ignored. You don't necessarily know when executing the INSERT what the next Id is going to be so you can't create a reference to a row that doesn't exist yet.
Change your mapping code to something like the following:
this.Property(x => x.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
this.HasRequired(x => x.CreatedByUser)
.WithMany();
You don't need to specify the foreign key if the name pattern matches (eg. CreatedByUser and CreatedByUserId).
Now when you insert a User you can specify the Id and the CreatedById. Although note that you must now always specify the Id to insert a new User. This is common practice if you are using GUIDs as Ids because you can just generate a new GUID without having to first query for the next "available" Id before creating a new object.
I'm trying to use Entity Framework code first for a simple database project and I run into a problem I simply cannot figure out.
I noticed EF was setting the ID for my tables automatically increasing by 1 each time, completely ignoring the value I entered manually for that field. After some searching it is my understanding that the right way to disable this behavior is doing:
modelBuilder.Entity<Event>().Property(e => e.EventID).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
However now I'm just getting this error and I have no idea why:
Unhandled Exception:
System.Data.Entity.Infrastructure.DbUpdateException: An error
occurred while updating the entries. See the inner exception for
details. ---
System.Data.UpdateException: An error occurred while updating the entries. See the inner exception for details. --->
System.Data.SqlClient.SqlException: Cannot insert explicit value for
identity column in table 'Events' when IDENTITY_INSERT is set to OFF.
If it's helpful, here is the POCO class in question:
public class Event
{
[Key, Required]
public int EventID { get; set; }
public string EventType { get; set; } //TODO: Event Type Table later on
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public virtual ICollection<Match> Matches { get; set; }
public virtual ICollection<EventParticipation> EventParticipation { get; set; }
}
Thanks in advance.
Since I prefer attributes, here the alternative for the sake of completeness:
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int Id { get; set; }
Note: This works also in EF Core.
By default Entity Framework assumes that an integer primary key is database generated (equivalent to adding the attribute HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity) or calling Property(e => e.EventID).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); in the Fluent API.
If you look at the migration that creates the table you should see this:
CreateTable(
"dbo.Events",
c => new
{
EventID = c.Int(nullable: false, identity: true),
//etc
})
.PrimaryKey(t => t.EventID );
Then you changed the model using the Fluent API to DatabaseGenerated.None. EF puts this in the migration:
AlterColumn("dbo.Events", "EventID", c => c.Int(nullable: false, identity: false))
And the sql generated is this:
ALTER TABLE [dbo].[Events] ALTER COLUMN [EventID] [int] NOT NULL
Which actually does diddly squat. Dropping the IDENTITY from a column is not trivial. You need to drop and recreate the table or create a new column, then you have to copy the data and fix up foreign keys. So it's not surprising that EF isn't doing that for you.
You need to work out how best to do it for yourself. You could roll back your migrations to 0 and re-scaffold from scratch now that you have specified DatabaseGeneratedOption.None, or you could change the migration manually to drop and recreate the table.
Or you could drop and recreate the column:
DropColumn("Customer", "CustomerId");
AddColumn("Customer", "CustomerId", c => c.Long(nullable: false, identity: false));
EDIT
Or you could Switch Identity On/Off With A Custom Migration Operation