How do I tell Entity (Code First) to not send the Key ID field to the database? - entity-framework

My code:
Models.Resource r = new Models.Resource();
r.Name = txtName.Text;
r.ResourceType = resTypes.Find(rt => rt.Name == "Content");
r.ResourceContents.Add(_resourceContent.Find(rc => rc.ID == _resourceContentID));
ctx.Resource.Add(r);
ctx.SaveChanges();
ctx.SaveChanges() causes the error:
Cannot insert explicit value for identity column in table 'Resources' when IDENTITY_INSERT is set to OFF.
Looking at what's being sent to SQL:
ADO.NET:Execute NonQuery "INSERT [dbo].[Resources]([ID], [Name], [Description], [IsOnFile],
[ContentOwnerAlias], [ContentOwnerGroup], [ResourceTypes_ID])
VALUES (#0, #1, #2, #3, #4, #5, NULL)"
My POCO Resource has ID as a Key:
public partial class Resource
{
public Resource()
{
}
[Key]
public int ID { get; set; }
And my Map code:
public class ResourceMap : EntityTypeConfiguration<Resource>
{
public ResourceMap()
{
// Primary Key
this.HasKey(t => t.ID);
How do I tell Entity to not send the Key ID field to the database?

If your PK is generated by the database (like an identity) you have to configure it in your Map.
public class ResourceMap : EntityTypeConfiguration<Resource>
{
public ResourceMap()
{
// Primary Key
this.HasKey(t => t.ID);
this.Property(t => t.ID).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
}
}

You do not need the HasKey(t => t.ID) Fluent API mapping or the [Key] Data Attribute because by convention EF will assume that an integer field named ID is the key and is database generated.
As an aside, I'd recommend that when you are not following conventions you should choose one method or the other - otherwise you are repeating yourself and when you want to change something you need to change it in 2 places.
I'm not sure why the field in the database isn't already database generated - maybe when you define the field via the fluent api you have to specify that too. What I do know is that in order to make EF change a key field to be database generated you will need to drop the table.
So - rollback the migration or drop the table / database, then remove the data attribute, remove the fluent mapping and recreate.
This issue is currently on a "backlog" in the entity framework. If you want to vote for it you can do that here: Migrations: does not detect changes to DatabaseGeneratedOption
Other References:
Identity problem in EF
Switching Identity On/Off With A Custom Migration Operation

Related

EF6: navigation property foreign key to foreign key

I have a legacy database which I cannot change with two entities with a foreign key property in each entity:
EntityOne
ForeignKey1 (pointing to EntityTwo.ForeignKey2)
EntityTwo
ForeignKey2 (pointing to EntityOne.ForeignKey1)
These foreign keys point to eachother. How can I map this relation with EF6? If it isn't possible, is there any workaround?
I tested with a test database which actually has a foreign key relation from EntityOne to EntityTwo and another from EntityTwo to EntityOne and auto generating the model, with code first from database, did not create any relationships between the two tables:
public partial class EntityFrameworkTesting : DbContext
{
public EntityFrameworkTesting()
: base("name=EntityFrameworkTesting")
{
}
public virtual DbSet<EntityOne> EntityOne { get; set; }
public virtual DbSet<EntityTwo> EntityTwo { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
}
}
With some additional trying I've succeeded to build a relationship between these entities. However, I had to delete the foreign key properties from my POCO classes to allow usage of the MapKey function. I would still like to access the foreign key, but when adding the foreign key property again, I receive an error stating: "ForeignKey2: Name: Each property name in a type must be unique. Property name 'ForeignKey2' is already defined.".
Basically I'm looking for the relationship functionality I have now, but with added foreign key property on my POCO class.
modelBuilder.Entity<EntityTwo>()
.HasRequired(entity => entity.EntityOne)
.WithRequiredDependent(entity => entity.EntityTwo)
.Map(m => m.MapKey("ForeignKey2"));
In this situation you want to use a shared primary key, meaning the PK of the dependent entity is also the FK to the principal entity (and references the principal entity's PK), enforcing a 1..0 relationship. You can view this answer to see how it's configured.
If you can't change your database schema, you're out of luck in EF6. EF6 does not support FK's referencing any candidate key other than the primary key.
EFCore supports FK's referencing alternate candidate keys, but still won't create two mutually referencing FK's - only the FK in the dependent table will be created (or so it seems via my limited testing just now). You can do so via the following FluentAPI configuration (assuming EntityOne is the principal entity):
modelBuilder.Entity<EntityOne>()
.HasAlternateKey( eo => eo.ForeignKey1 );
modelBuilder.Entity<EntityTwo>()
.HasOne( et => et.EntityOne )
.WithOne( eo => eo.EntityTwo )
.IsRequired()
.HasForeignKey<EntityTwo>( et => et.ForeignKey2 )
.HasPrincipalKey<EntityOne>( eo => eo.ForeignKey1 );
EntityOne.ForeignKey1 should be nullable. When testing, I had to manually specify the type parameters for the Has*Key methods, not sure why.

EF migration generating duplicated FK with different name

I'm Trying to rename the Default FK, but Code First Migration keep generating a second FK to the same table with a different name, messing up the Table schema.
The FK Model has a PK named Id, I just want to keep the convention required by my client, changing the name for Id(something).
1) Generated migration:
2) Mapping:
What should I do?
If your CorpoGestor entity exposes a property for the foreign key, use HasForeignKey instead of Mapand MapKey.
HasRequired(x => x.Conselho)
.WithMany()
.HasForeignKey(x => x.IdConselho);
Another solution: you can use the ForeignKey attribute in the property, instead of mapping the relation in the CorpoGestorMap class:
public class CorpoGestor
{
...
public int IdConselho { get; set; }
[ForeignKey("IdConselho")]
public virtual Conselho Conselho { get; set; }
}
A warning: using attributes in the entities is conceptually not so cool as implementing the code in the EntityTypeConfiguration mapping classes, because you are polluting your entities with data layer code that should be kept in the EF classes.

How do i assosiate my PICO class with a specific table in my database with Entity Framework 5

I have a table in my db (a many to many table) that two classes A and B have created like this.
this.HasMany(t => t.TrafficImageQuestions)
.WithMany(t => t.TrafficImages)
.Map(m =>
{
m.ToTable("TrafficImage_Answers");
m.MapLeftKey("TrafficImagesGuid");
m.MapRightKey("TrafficImageQuestionsId");
});
Now i would like to assosiate my custom class to this same table "TrafficImage_Answers", the class offcause have the left and right key and then also a 3. custom property.
(i did add the column to the database "Answer")
public class TrafficImageAnswer
{
public System.Guid TrafficImageGuid { get; set; }
public int TrafficImageQuestionId { get; set; }
public byte Answer { get; set; }
}
I am doing this as i want entity model to keep track of my many to many relationship of A and B and still be able to look up the 3. property Answer that is in the database.
What i have tried
I tried to do the following:
this.Property(t => t.TrafficImageQuestionId)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
this.Property(t => t.TrafficImageGuid)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
// Table & Column Mappings
this.ToTable("TrafficImage_Answers");
But i get that the table already exists, logic. I need to tell it that it just should use that table and not try to create it.
(im doing this with DB mitigrations in EF 5 and Package manager).
That is not supported. If you want to have additional field in the junction table for many-to-many relation you cannot map it as many-to-many any more. Each table can be mapped only once but mapping table to entity and to many-to-many relation in the same time makes it mapped twice.
You must change your TrafficImageQuestions and TrafficImages to use one to many relations with TrafficImageAnswer instead of many-to-many relation with each other.

Entity Framework auto incrementing field, that isn't the Id

I know this isn't the most ideal solution, but I need to add an auto incrementing field to one of my EF Code First objects. This column id NOT the Id, which is a guid.
Is there anyway for me to define the auto incrementing field in code, or would creating the column myself and defining in the DB that its auto incrementing work?
You can annotate that property with DatabaseGenerated(DatabaseGeneratedOption.Identity). EF allows only single identity column per table.
public class Foo
{
[Key]
public Guid Id { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long Bar { get; set; }
}
Old post thought I would share what I found with Entity Framework 6.1.3.
I created a simple data layer library using C# and .NET Framework 4.6.1, added a simple repository/service class, a code first context class and pointed my web.config file to a local SQL Express 2014 database.
In the entity class I added the following attribute constructor to the Id column:
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
Then I created a new migration by typing the following in Visual Studio 2015 Package Manager:
Add-Migration
Give the migration a name and then wait for the DbMigtation class to be created. Edit the class and add the following CreateTable operation:
CreateTable(
"dbo.Article",
c => new
{
Id = c.Guid(nullable: false, identity: true),
Title = c.String(),
Content = c.String(),
PublishedDate = c.DateTime(nullable: false),
Author = c.String(),
CreateDate = c.DateTime(nullable: false),
})
.PrimaryKey(t => t.Id);
}
The above table is an example the key point here is the following builder annotation:
nullable: false, identity: true
This tells EF to specifiy the column as not nullabe and you want to set it as an identity column to be seeded by EF.
Run the migration again with the following command:
update-database
This will run the migration class dropping the table first (Down() method) then creating the table (Up() method).
Run your unit tests and/or connect to the database and run a select query you should see your table in its new form, add some data excluding the Id column and you should see new Guid's (or whatever data type your choose) to be generated.
For those stumbling onto this question for EF Core, you can now create an auto-incrementing column with your model builder as follows:
builder.Entity<YourEntity>().Property(e => e.YourAutoIncrementProperty).UseNpgsqlIdentityAlwaysColumn();
Reference: https://www.npgsql.org/efcore/modeling/generated-properties.html

Entity Framework code first unique column

I am using Entity Framework 4.3 and using Code Fist.
I have a class
public class User
{
public int UserId{get;set;}
public string UserName{get;set;}
}
How do I tell Entity Framework that UserName has to be unique when creating database table?
I would prefer to use data anotations instead of configuration file if possible.
In Entity Framework 6.1+ you can use this attribute on your model:
[Index(IsUnique=true)]
You can find it in this namespace:
using System.ComponentModel.DataAnnotations.Schema;
If your model field is a string, make sure it is not set to nvarchar(MAX) in SQL Server or you will see this error with Entity Framework Code First:
Column 'x' in table 'dbo.y' is of a type that is invalid for use as a key column in an index.
The reason is because of this:
SQL Server retains the 900-byte limit for the maximum total size of all index key columns."
(from: http://msdn.microsoft.com/en-us/library/ms191241.aspx )
You can solve this by setting a maximum string length on your model:
[StringLength(450)]
Your model will look like this now in EF CF 6.1+:
public class User
{
public int UserId{get;set;}
[StringLength(450)]
[Index(IsUnique=true)]
public string UserName{get;set;}
}
Update:
if you use Fluent:
public class UserMap : EntityTypeConfiguration<User>
{
public UserMap()
{
// ....
Property(x => x.Name).IsRequired().HasMaxLength(450).HasColumnAnnotation("Index", new IndexAnnotation(new[] { new IndexAttribute("Index") { IsUnique = true } }));
}
}
and use in your modelBuilder:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// ...
modelBuilder.Configurations.Add(new UserMap());
// ...
}
Update 2
for EntityFrameworkCore see also this topic: https://github.com/aspnet/EntityFrameworkCore/issues/1698
Update 3
for EF6.2 see: https://github.com/aspnet/EntityFramework6/issues/274
Update 4
ASP.NET Core Mvc 2.2 with EF Core:
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Unique { get; set; }
EF doesn't support unique columns except keys. If you are using EF Migrations you can force EF to create unique index on UserName column (in migration code, not by any annotation) but the uniqueness will be enforced only in the database. If you try to save duplicate value you will have to catch exception (constraint violation) fired by the database.
In EF 6.2 using FluentAPI, you can use HasIndex()
modelBuilder.Entity<User>().HasIndex(u => u.UserName).IsUnique();
From your code it becomes apparent that you use POCO. Having another key is unnecessary: you can add an index as suggested by juFo.
If you use Fluent API instead of attributing UserName property your column annotation should look like this:
this.Property(p => p.UserName)
.HasColumnAnnotation("Index", new IndexAnnotation(new[] {
new IndexAttribute("Index") { IsUnique = true }
}
));
This will create the following SQL script:
CREATE UNIQUE NONCLUSTERED INDEX [Index] ON [dbo].[Users]
(
[UserName] ASC
)
WITH (
PAD_INDEX = OFF,
STATISTICS_NORECOMPUTE = OFF,
SORT_IN_TEMPDB = OFF,
IGNORE_DUP_KEY = OFF,
DROP_EXISTING = OFF,
ONLINE = OFF,
ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON
) ON [PRIMARY]
If you attempt to insert multiple Users having the same UserName you'll get a DbUpdateException with the following message:
Cannot insert duplicate key row in object 'dbo.Users' with unique index 'Index'.
The duplicate key value is (...).
The statement has been terminated.
Again, column annotations are not available in Entity Framework prior to version 6.1.
Note that in Entity Framework 6.1 (currently in beta) will support the IndexAttribute to annotate the index properties which will automatically result in a (unique) index in your Code First Migrations.
Solution for EF4.3
Unique UserName
Add data annotation over column as:
[Index(IsUnique = true)]
[MaxLength(255)] // for code-first implementations
public string UserName{get;set;}
Unique ID
,
I have added decoration [Key] over my column and done.
Same solution as described here: https://msdn.microsoft.com/en-gb/data/jj591583.aspx
IE:
[Key]
public int UserId{get;set;}
Alternative answers
using data annotation
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Column("UserId")]
using mapping
mb.Entity<User>()
.HasKey(i => i.UserId);
mb.User<User>()
.Property(i => i.UserId)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity)
.HasColumnName("UserId");