Entity Framework code first unique column - entity-framework

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");

Related

ASP.NET MVC 5, Entity Framework db.savechanges() returns wrong inserted id

I'm using EF and ASP.NET MVC 5, and when inserting into database using
dbcontext.SaveChanges(mymodel)
the returned id is always "1", however in the SQL database, the inserted record's id is "3" (before inserting I deleted the first two records in the database table to clear the table).
Could anyone tell me how to fix this? I feel EF is not update to date with database or am missing any settings to sync both? Many thanks in advance...
My code below...
if (ModelState.IsValid)
{
stodoc.StockOutDocument_Serial = srlnum;
stodoc.StockOutDocument_date = inview.InventoryDate;
db.StockOutDocuments.Add(stodoc);
int stodocid = db.SaveChanges();
}
The ID of the newly created record doesn't come from the SaveChanges call. SaveChanges will update all modified/inserted/deleted records.
Provided you have configured your entity as recognizing it's ID is DatabaseGeneratedOption.Identity then to get the newly inserted ID:
stodoc.StockOutDocument_Serial = srlnum;
stodoc.StockOutDocument_date = inview.InventoryDate;
db.StockOutDocuments.Add(stodoc);
db.SaveChanges();
int stodocid = stodoc.stodocid; // Will be populated once SaveChanges is called.
If the stodoc.stodocid property is not updated, check that you have configured it in the mapping as an Identity column:
If you used attributes in the entity:
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int stodocid { get; set; }
or via entity configuration:
EF Core:
builder.HasKey(x => x.stodocid);
builder.Property(x => x.stodocid).UseSqlServerIdentityColumn();
EF6:
HasKey(x => x.stodocid)
.Property(x => x.stodocid)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);

EF Core 2.0/2.1 - How to efficiently handle large, infrequently accessed columns?

I have a table as follows:
CREATE TABLE MyTable
(
ID INT NOT NULL PRIMARY KEY,
NAME VARCHAR(50) NOT NULL,
LARGEBLOB VARBINARY(MAX) NULL
)
With an Entity defined as:
public class Entity
{
public int Id {get;set;}
public string Name {get;set;}
public virtual byte[] LargeBlob {get;set;}
}
99% of my use cases involve displaying ID and NAME only.
1% of the time I need LARGEBLOB.
Is there any way I can mark LargeBlob as Lazily Loaded so to avoid
huge wasted data transfers? Alternatively, are there other ways of
achieving the same outcome?
I tried splitting into 2 tables with a 1->[0|1] relationship as follows:
CREATE TABLE MyTable
(
ID INT NOT NULL PRIMARY KEY,
NAME VARCHAR(50) NOT NULL,
LARGEBLOBID INT NULL
)
CREATE TABLE MySubTable
(
ID INT PRIMARY KEY,
LARGEBLOB VARBINARY(MAX) NOT NULL
)
with entities
public class Entity
{
public int Id { get; set; }
public string Name { get; set; }
public virtual LargeBlob LargeBlob { get; set; }
}
public class LargeBlob
{
public int Id { get; set; }
public virtual byte[] Blob { get; set; }
}
That did work in so far as lazy loading was concerned, but I tried all manner of inverse relationship / foreign key tags, HasOne, OwnsOne, OnDelete(Cascade) in all kinds of combinations, but I couldn't achieve what I wanted to achieve. Just to recap, that would be:
Blob is loaded only when the LargeBlob property is actually derefenced.
If entity.LargeBlob property gets set to a new LargeBlob, the (now "orphaned" ) old LargeBlob gets deleted from the database.
If the entity gets deleted, the related large blob gets deleted.
Quick Update re: versions &c
Note: I'm using VS 2017 15.6.2, .net core 2.0, with EF core 2.1 (to get at least the possibility of some lazy loading). Nuget packages:
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="2.1.0-preview1-final" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.1.0-preview1-final" PrivateAssets="All" />
I tried splitting into 2 tables with a 1->[0|1] relationship as follows
But by putting the FK in the Entity you actually did the opposite - [0|1]->1 relationship.
To get the desired relationship, the FK must be at LargeBlog. It could be a separate property (column), but the most appropriate is to use the Id property as both PK and FK (the so called shared PK association). You can do it with the following fluent configuration:
modelBuilder.Entity<Entity>()
.HasOne(e => e.LargeBlob)
.WithOne()
.HasForeignKey<LargeBlob>(e => e.Id);
Once you do that, since the whole purpose of doing it was to get separate controllable (eager, explicit or lazy when available) load behavior, it can be seen that the separate table is not really needed - the "entity" containing the blob data can be embedded inside the same table using the table splitting which is achieved by simply adding the following to the above configuration:
modelBuilder.Entity<Entity>().ToTable("MyTable");
modelBuilder.Entity<LargeBlob>().ToTable("MyTable");
Note that while the most logical choice seems to be owned type, unfortunately currently owned types are always loaded (similar to EF6 complex types), so they cannot be used to achieve controllable load behavior.
You should only select the columns you need to save bandwidth:
var entity = await dbContext.Entities
.Where(...)
.Select(e => new
{
Id = e.Id,
Name = e.Name,
LargeBlob = null,
})
.FirstOrDefaultAsync();
and whenever you really need the LargeBlob column, load it manually
entity.LargeBlob = await dbContext.Entities
.Where(e => e.Id == entity.Id)
.Select(e => e.LargeBlob)
.SingleOrDefaultAsync();
You can delete an entity without loading the whole entity, just the Id (and the concurrency token, if present on the entity) suffices
var entity = new Entity { Id = removeEntityId };
dbContext.Entities.Remove(entity);
await dbContext.SaveChangesAsync();

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.

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

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

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