Rebuilding Sql Server indexes name invalidate the hardcoded value in EF - c#

In EF Context file,i have hard coded key name/index name in OnModelCreating. DBA rebuilt those indexes/keys with different names. Do i have to update those reference in code again ? or is there any other approach ?
protected override void OnModelCreating(ModelBuilder modelBuilder){
modelBuilder.Entity<Customert>(entity =>
{
entity.HasKey(e => e.custId)
.HasName("PK__cust__4E739DAA");
}
}

As an option you can use Key attribute directly on entity property, for instance
using System.ComponentModel.DataAnnotations;
public class Customer
{
[Key]
public int CustomerId {get;set;}
}
documentation

Related

Entity Framework Core does not respect Identity columns

Entity Framework is not respecting my Identity columns. It insists on trying to insert a value into an Identity (auto-increment) column in my MS SQL DB, which is obviously an error since the DB is supposed to supply the value.
System.Data.SqlClient.SqlException: 'Cannot insert explicit value for identity column in table 'Assignee' when IDENTITY_INSERT is set to OFF.'
Why is it trying to do that? I've paired it down to a schema involving one table and one column:
CREATE TABLE [dbo].[Assignee](
[AssigneeID] INT IDENTITY(-1, 1) NOT NULL
CONSTRAINT [Assignee$PrimaryKey] PRIMARY KEY CLUSTERED
( [AssigneeID] ASC ))
After publishing this schema to my local DB I use Scaffold-DbContext to generate entity and context classes. The generated Assignee class contains just this public property.
public int AssigneeId { get; set; }
The context only refers to Assignee here:
modelBuilder.Entity<Assignee>(entity =>
{
entity.Property(e => e.AssigneeId).HasColumnName("AssigneeID");
});
Searching around I see people claiming that for E.F. to respect Identity columns, the context should configure the property with ValueGeneratedOnAdd(). In other words, the line in the context class should read:
entity.Property(e => e.AssigneeId).HasColumnName("AssigneeID")
.ValueGeneratedOnAdd();
I have two problems with this:
I'm starting with an existing DB and generating entity classes. If I need ValueGeneratedOnAdd() then why isn't Scaffold-DbContext generating it?
Even if I manually edit the generated context class and add ValueGeneratedOnAdd() it still doesn't work with the same error.
Elsewhere I see suggestions to use UseSqlServerIdentityColumn(). That also doesn't work for me. Points 1 and 2 still apply.
Any help would be greatly appreciate. Please don't suggest that I use IDENTITY_INSERT as that defeats the entire point of using auto-increment columns.
(I am using Entity Framework Core 2.2.3 and Microsoft SQL Server 14)
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Todo>(entity =>
{
entity.Property(x => x.Id)
.HasColumnName("id")
.HasColumnType("int")
.ValueGeneratedOnAdd()
**.UseIdentityColumn();**
}
Try do this.
Ef Core Dependency : Microsoft.EntityFrameworkCore.SqlServer
This works for me:
modelBuilder.Entity<Assignee>().Property(e => e.AssigneeId).UseIdentityColumn();
So UseIdentityColumn() is the key.
I'm using Microsoft.EntityFrameworkCore.SqlServer v3.1.8.
Short version
We are getting and experiencing different results here one can reproduce the issue, others can not. My experience it depends on if the Id property's value is 0 or not.
Detailed version
My experience, that the default behavior (based on name convention) is definitely working, so in case you are naming your db entity's attribute (C# property) to Id or EntityNameId it should work. No C# entity class attributes neither OnModelCreating config is necessary.
The same time if the issue is there neither No C# entity class attributes neither OnModelCreating config will fix it.
...because if the Id property's value is not 0, the generated SQL will contain the explicit field name and value, so we got the error.
This is clearly and issue in EF core, but workaround is easy..
For DB first try adding [key] as a data annotation
With Data annotation
[Key]
public int AssigneeId { get; set; }
fluent API
modelBuilder.Entity<Assignee>()
.HasKey(o => o.AssigneeId);
See here or here if you want to use fluent API
I've tried to reproduce this issue based on your example but it appears to work just fine. I did not use Scaffold though, just coded class and I tried the model creating code you had and it hasn't had an issue. I suspect there has to be more to this though because with just the "Assignee" class, EF convention is expecting an "Assignees" table, so I suspect there is more mapping being set up.
Tested with EF Core 2.0.3 and 2.2.4
DB: used the OP's script.
Entity:
[Table("Assignee")]
public class Assignee
{
public int AssigneeId { get; set; }
}
I had to use the Table attribute to map to the table name.
Context:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Assignee>(entity =>
{
entity.Property(e => e.AssigneeId).HasColumnName("AssigneeID");
});
}
as-per OP comment.
Test:
[Test]
public void TestIncrement()
{
using (var context = new TestDbContext())
{
var newItem = new Assignee();
context.Assignees.Add(newItem);
context.SaveChanges();
}
}
Works as expected.
However, what I'd normally have for the entity:
[Table("Assignee")]
public class Assignee
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity), Column("AssigneeID")]
public int AssigneeId { get; set; }
}
And then nothing for this column needed in the context OnModelCreating override.
I suspect that there is some additional configuration lurking somewhere given there is no mention of the table name issue, either manually added or via scaffold that is goofing up EF. I was full-on expecting EF to fail without the Key/DbGenerated attributes, but it seemed to work just fine.
Edit: Also tried this with scafolding running Scaffold-DbContext across the existing schema. Again, worked without an issue.
For comparison against your tests:
Generated DbContext: (Unaltered save removing the warning and connection string details.)
public partial class AssigneeContext : DbContext
{
public AssigneeContext()
{
}
public AssigneeContext(DbContextOptions<AssigneeContext> options)
: base(options)
{
}
public virtual DbSet<Assignee> Assignee { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
optionsBuilder.UseSqlServer("Data Source=machine\\DEV;Initial Catalog=Spikes;uid=user;pwd=password;MultipleActiveResultSets=True");
}
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.HasAnnotation("ProductVersion", "2.2.4-servicing-10062");
modelBuilder.Entity<Assignee>(entity =>
{
entity.Property(e => e.AssigneeId).HasColumnName("AssigneeID");
});
}
}
Generated Entity: (Unaltered)
public partial class Assignee
{
public int AssigneeId { get; set; }
}
I did figure out why my table annotation was needed. EF Core (Not sure if applies to EF6 as well) was basing the convention for the table name on the DbSet variable name in the DbContext. I couldn't see any config difference with the scaffold generated context and my own, except the DbSet name. I renamed my original DbContext's DbSet name to "Assignee" and it worked without the Table attribute.
That said, based on the information present your code should work. Something is lurking in the details because this example does work so you will need to provide more detail about an example that definitely doesn't work in your case.

the correct way to map identity base class with fluent api in ef core?

How is it supposed that i have to map the identity base class in EF Core if i want to entity be a primary key
I based in the project example of Vaughn Vernon (example) in this example him create a identity class like that:
public class ProductId : Identity
{
public ProductId()
: base()
{
}
public ProductId(string id)
: base(id)
{
}
}
and here is his implementation
public class Product {
public ProductId ProductId {get; private set;}
}
My doubt is what is the correct way to map this? because i was try to map directically modelBuilder.Entity<Product>().HasKey(p => p.ProductId); but in migration EF Core tell me
Cannot call Property for the property 'ProductId' on entity type 'Product' because it is configured as a navigation property.
Property can only be used to configure scalar properties.
i know that i can use OwnsOne but if i do that way i will don't create a primary key of identity, right?
Your objects should look like the following
Object Class
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
...
}
By default entity framework will look for an object named Id or ProductId and will automatically make it the primary key. So while it is not needed to set the key in the DbContext you are able to do it. I generally do not set the Id unless it is a different name.
DbContext
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<Product>().HasKey(x => x.Id);
}
** UPDATE **
If you are not setting the DbSets in your db context, then you indeed will have to set the id in the OnModelCreating call so the migrations will recognize the object as an Entity object

Set EntityKey manually without accessing the database

In my application (database-first) the primary keys are always created by client, not by the underlying database.
In Entity Framework v4 each Entity had it's EntityKey property that could be set manually.
However, I can't find it anymore in EF6?
Is there any specific reason why?
I don't want to call ctx.SaveChanges() only to have an Entity's key set as this means a call to the database.
I'm also wondering why ObjectContext.CreateEntityKey is gone?!
If You're using POCO's - here's a sample how it could be done
public class ChronosItem
{
[Column, Key, Required, MaxLength(26), DatabaseGenerated(DatabaseGeneratedOption.None)]
public string Id { get; set; }
....
In case You cant adjust Your entities
override the "OnModelCreating" method on Your context.. like so..
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<ChronosItem >()
.HasKey(p => p.Id)
.Property(p => p.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);

ModelValidationException: Name 'AnonymousUID' cannot be used in type 'CodeFirstDatabaseSchema.AnonymousUID'

I am using Entity Framework to model an existing database. One of the database tables contains a column with the same name as the table, AnonymousUID.
I use the Entity Framework Power Tools function Reverse Engineer Code First to generate the model classes and mappings. The reverse engineering procedure automatically renames the AnonymousUID class member (to AnonymousUID1) to avoid that a member name is the same as the class name. The generated model class thus looks like this:
public partial class AnonymousUID
{
public string UID { get; set; }
public string AnonymousUID1 { get; set; }
}
and the EF mapping constructor is implemented like this:
public AnonymousUIDMap()
{
// Primary Key
this.HasKey(t => t.UID);
// Properties
this.Property(t => t.UID).IsRequired().HasMaxLength(64);
this.Property(t => t.AnonymousUID1).IsRequired().HasMaxLength(64);
// Table & Column Mappings
this.ToTable("AnonymousUID");
this.Property(t => t.UID).HasColumnName("UID");
this.Property(t => t.AnonymousUID1).HasColumnName("AnonymousUID");
}
The database context class is implemented like this:
public partial class MyDbContext : DbContext
{
// Constructors...
public DbSet<AnonymousUID> AnonymousUIDs { get; set; }
...
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new AnonymousUIDMap());
...
}
}
This is all good and well, and the code builds without problems. But when I try to access arbitrary contents of the database:
using (var context = new MyDbContext())
{
var foos = from foo in context.Foos select foo;
...
}
the following exception is nonetheless thrown:
System.Data.Entity.ModelConfiguration.ModelValidationException :
One or more validation errors were detected during model generation:
\tAnonymousUID: Name: Name 'AnonymousUID' cannot be used in type
CodeFirstDatabaseSchema.AnonymousUID'. Member names cannot be the
same as their enclosing type.
There is obviously some additional mapping build-up going on in CodeFirstDatabaseSchema, and this procedure is not able to avoid the class/member name clash.
Why does this error occur? After all, the reverse engineering procedure managed to circumvent the naming issue.
Without modifying the schema of the already established database, is there some way I can avoid this exception from being thrown?
I am using Entity Framework 6.0 (pre-release) from Nuget in a .NET Framework 4 project.
As I guessed at in the comment above, change the column name to start with a lowercase 'a'
...HasColumnName("anonymousUID");
Let's hope this is a pre release defect and is fixed in the RTM ;-)
Is the code you displayed the only place you're using the AnonymousUID name? What is the name of your DbSet in the generated DbContext?
I built a test app, and didn't get any errors, here it in it's entirety:
public partial class AnonymousUID
{
[Key]
public int UID { get; set; }
public string AnonymousUID1 { get; set; }
}
public class Model : DbContext
{
public DbSet<AnonymousUID> AnonymousUIDs { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<AnonymousUID>()
.Property(a => a.AnonymousUID1)
.HasColumnName("AnonymousUID");
modelBuilder.Entity<AnonymousUID>()
.ToTable("AnonymousUID");
}
}
class Program
{
static void Main(string[] args)
{
var model = new Model();
var a = new AnonymousUID();
a.AnonymousUID1 = "hello world";
model.AnonymousUIDs.Add(a);
model.SaveChanges();
var applications = model.AnonymousUIDs.ToList();
Console.WriteLine(applications.Count);
Console.ReadLine();
}
}
Created this database:
It was able to create, insert, and then display the count of the table.
I had a similar issue, but I could not rename the column because it would break legacy code. The type names that Power Tools generated are all lowercase. I changed the capitalization on the conflicting types, and it worked.
I suspect that CodeFirstDatabaseSchema is maintaining lowercase property names, but I am not sure. All I know is that changing my lowercase type names to properly capitalized type names fixed it. I hope that is helpful for someone.

Does Entity Framework 4 Code First have support for identity generators like NHibernate?

This question, asked a year ago, is similar:
Does the Entity Framework 4 support generators for id values like NHibernate?
But what I'd like to know is if the code first CTP adds support for identity generation strategies. If not, does anyone know a good extension point in EF to implement something similar?
I'm currently working with model classes which use GUID as the identifier. When inserting using EF they retain their Guid.Empty initial values. I know that you can set a default value for the column in the DB to newid() but that defeats the purpose of client-side identity generation.
Is Entity Framework just not mature enough to be used in a distributed, disconnected system?
No, Entity framework code-first is still just nice wrapper around EFv4. There are no NHibernate like generators. If you want client side Id generator you will have to override SaveChanges in derived DbContext and implement your own logic of assigning Ids to new entities.
Edit:
Some high level example:
public class Context : DbContext
{
// Helper for example
// DO NOT USE IN REAL SCENARIOS!!!
private static int i = 0;
public DbSet<MyEntity> MyEntities { get; private set; }
public Context()
: base("connection")
{
MyEntities = Set<MyEntity>();
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<MyEntity>().HasKey(e => e.Id);
// Turn off autogeneration in database
modelBuilder.Entity<MyEntity>()
.Property(e => e.Id)
.HasDatabaseGeneratedOption(HasDatabaseGeneratedOption.None);
// Other mapping
}
public override int SaveChanges()
{
foreach (var entry in ChangeTracker.Entries<MyEntity>()
.Where(e => e.State == EntityState.Added))
{
// Here you have to add some logic to generate Id
// I'm using just static field
entry.Entity.Id = ++i;
}
return base.SaveChanges();
}
}
public class MyEntity
{
public int Id { get; set; }
// Other properties
}
No.
Mine Entity Framework 4.1.10715 installed by NuGet.
maybe you could use attribute
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id {get;set;}
please see (The full list of annotations supported in EF 4.1 : CTRL+F in page) here.
[Key]
public string Id { get; set; }
and then use new GUID ToString
Units.Add( new Unit(){Id=Guid.NewGuid().ToString(), Name="123"});

Categories