I am using Entity Framework 5 code-first and I have overridden the SaveChanges method. In SaveChanges, I want to identify any entities with an EntityState == EntityState.Added (I can do this easily enough) however I then want to identify any columns in those entities which have been defined as a primary key, have their HasDatabaseGeneratedOption property set to DatabaseGeneratedOption.None and currently have Null value.
I need to identify these columns as my database currently has some columns defined as primary keys which need to be manually populated via code. I figured I could tackle this population of columns in SaveChanges on an insert but am stumped as to how to identify them.
How do I query column definitions in SaveChanges? I obviously know how to examine the data value
You need to look at the data model, get the property that holds the primary key and see if it has the attribute you want. See an example here: http://weblogs.asp.net/ricardoperes/entity-framework-metadata.
However, this will not work if you are not using attributes, but instead customizing the model in OnModelCreating.
Related
I have a database-first model using Entity Framework 6.2.0 which has the following association:
As you see, the OnDelete property on both ends is set to None. The corresponding relation in SQL server is shown below:
Although there is no explicit setting to set null on delete, when I try to remove an object from Plannings (primary table), EF sets all foreign key records in the table Waybill to null.
In the same database and model, I have the following foreign key and corresponding association:
All circumstances are the same as earlier. But in this case, when I try to remove an object from Products, it fails because of conflict with foreign key constraint (as I expect).
Why these two similar cases have different behavior? How can I completely disable set null on delete in my database-first model? I know that in code-first model, we can delete conventions using the following:
modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
But I could not find any way to do this in database-first.
I use code first of Entity framework. There are two classes "Question" and "User". I defined a relationship as below:
this.HasRequired(v => v.Creator).WithMany(v => v.Questiones)
.HasForeignKey(v => v.CreatorId).WillCascadeOnDelete(false);
After gernerating the database I found that it always create foreign key between Id of User and CreatorId of Question. Because of lower performance of FK(and other reason),I want to define navigation property relationship without setting foreign key in database? Delete FK after EF created it?
If cannot do this using fluent api, could you tell me why EF designed in this way please?
About the lower performance of FK. I have a User table with 5 Million records in it. when I insert a Question into db, since the db check the question.CreatorId validation from User table, it always slower than without FK.
And there are many other reasons that I need to remove FK.
I think I am somewhat obsession because I think that deleting FK after created it is strangely and ugly. What i want is implementing this by using something like WithoutForeignKey in fluent api:
this.HasRequired(v => v.Creator).WithMany(v => v.Questiones)
.WithoutForeignKey(v => v.CreatorId).WillCascadeOnDelete(false);
Without questioning why are you trying to do this strange thing and going just to the answer: you could delete fk constraint after generated, or you could use migrations and remove FK generation from the migration code.
SQL code generated when traversing nav properties will work even if fk constraint doesn't exist, except for cascade deleting
If you want a relationship between two tables, you need to define a foreign key. No way around it. Even if you use Map() in fluent api, you can only hide the foreign key in your model, in the background EF will still use it and it will exist in the database.
Also I don't get what you mean by "performance" of foreign key? One extra (likely small) column won't make a difference. If you mean the navigation properties for the performance part, you can do 3 things:
Don't include them in your model
Make them non-virtual to disable lazy loading
Disable lazy loading all together with ctx.Configuration.LazyLoadingEnabled = false;
If you don't want to tell db about relation and treat both entities as not related (I wonder why), then just ignore these navigation properties and FK field. Note that you will be responsible for managing related entities: saving and loading them from db, updating ids etc
this.Ignore(q => q.Creator);
this.Ignore(q => q.CreatorId);
And you also need to ignore other side of relation, otherwise EF will generate FK column with default name Creator_CreatorId. So in Creator entity configuration:
this.Ignore(c => c.Questiones);
Violation of PRIMARY KEY constraint 'PrimaryKeyId'. Cannot insert duplicate key in object 'dbo.Table'. The duplicate key value is (xxx).\r\nThe statement has been terminated.
This has been occurring off and on for several weeks now, and every time I think I have it fixed, and it pops up a couple days later. I am using:
dbContext.Table.Add(myObject);
dbContext.SaveChanges();
This is in a using statement, trying to add an object with a current id of 0. The PrimaryKeyId is the identity in the table, and is set to auto-increment by 1. However, Entity Framework seems to be taking a random Id and trying to assign it to my object and then add said object to the database.
This only happens on this one table and this same process is used on many other tables without any problems. The table being acted on is set up identical to other tables where this process is being used without error. Any ideas as to what could be causing this? To clarify, Entity Framework appears to be attempting to assign an already existing Primary Key to a new object.
Solution to my specific problem: Tar and feather a DBA
Explanation: While running a lengthy/complex import script, our DBA set it up to reseed the table at x, which is way below the current value in the identity column. So there technically hasn't been a problem these past several weeks, it was just human error. This question could/should/maybe ought to be dragged out behind a woodshed and put out of its misery. The tarring/feathering is his suggestion by the way (I do not endorse abuse of coworkers without their consent).
In the event that this question isn't deleted, the recommended fix is to check the current identity value on the table by using
select ident_current('tableName')
and comparing it to the highest value in the table. Especially if there are manual imports/modifications being done through a script where the seed might be manually reset.
This should prove helpful (Particularly the paragraph I have made bold): Taken from Working with Entity Keys
Entity Keys and Added Objects
When a new entity is created, the Entity Framework defines temporary key and sets the IsTemporary property to true. When you call the SaveChanges method, the Entity Framework assigns a permanent key and sets the IsTemporary property to false.
If the corresponding column value is an identity that is generated in the database, set the StoreGeneratedPattern attribute of the property element of an entity in the storage model to Identity. When the Entity Data Model tools generate a data model from an existing data source, the StoreGeneratedPattern attribute is added to each property element (CSDL) element that represents an identity or a computed column in the data source. The Entity Framework replaces the value of the property in a temporary key with the identity value that is generated by the data source after SaveChanges is called.
The following details the internal process that replaces the temporary key with a permanent key that contains the server-generated values:
The entity object is constructed.
At this point the key properties all have default values, either null or 0.
The new object is added to the ObjectContext either by calling the AddObject method on ObjectContext or ObjectSet or by adding an object to the collection of objects on the "many" end of the relationship.
At this point, the Entity Framework generates a temporary key, which is used to store the objects in the ObjectStateManager.
SaveChanges is called on the ObjectContext.
An INSERT statement is generated by the Entity Framework and executed on the data source.
If the INSERT operation succeeds, server-generated values are written back to the ObjectStateEntry.
The ObjectStateEntry updates the object with the server-generated value.
When AcceptChanges is called on the ObjectStateEntry, a permanent EntityKey is computed by using the new server-generated values.
AcceptChanges is called automatically at the end of the SaveChanges execution, or when the SaveChanges method is called with the AcceptAllChangesAfterSave flag.
The ObjectStateManager replaces all instances of the temporary key with the new permanent key.
I need to recreate a database with exactly the same values it has been originally created. So I need to add records with a pre-defined PK value. In this case, the PK is Identity in the database and when I try to define it's value, it is simply ignored, getting its value from the identity. No error is raised but the PK value that I supply is ignored.
example:
Category category = new Category()
{
CategoryID=1,
CategoryName="Beverages",
Description="Soft drinks, coffees, teas, beers, and ales"
};
ctx.Categories.Add(category);
ctx.SaveChanges();
Notes:
I'm using POCO, code first, so, I don´t have an EDMX Model to configure.
I don´t want to use ctx.Database.ExecuteSqlCommand(). I wish to maintain an Database agnostic approach.
In this case, the PK is Identity
In such case you should never manually insert its value. Once you set column as identity DB should be responsible for controlling the Id. Because of that there is no way to pass the value from EF (unless you want to break other functionality). You must use ExecuteSqlCommand and create complex SQL which will:
Turn on identity insert for the table
Insert record
Turn off identity insert for the table
Inserting value into identity column must be allowed by SET IDENTITY_INSERT tableName ON
I don't know if you scenario will let you do this, but if you define a composite key like as follows:
modelBuilder.Entity<Category>().HasKey(s => new { s.CategoryID, s.Name });
(using HasKey while running the DbContext.OnModelCreating method and EF 4.1 Code First), then you actually can control which values get inserted when you save the POCO object to the database.
I will say that, however, I would agree with Ladislav insofar as that the primary key values you are trying to maintain here are conceptually really more like data than record identifiers, and should be treated as such. Meaning, treat them as just data fields, and create a new primary key field on your POCO class in order to uniquely identify database records. e.g. for Category
public Int32 PK {get; set;}
and be sure to indicate it's intended to be the PK field from OnModelCreating
modelBuilder.Entity<Category>().HasKey(c => c.PK)
In my application I have an entity which is being used essentially as a complex many to many between my User and Project entities. I am trying to determine how to figure out if my service layer needs to add the entity to the context or attach the entity (for updating an existing entity) and I am at a loss of how.
This is easy to determine for most of my entities because of the Int Id field, if it's zero the database hasn't given it an identity value yet. This is not possible if it's a composite primary key.
Does anyone have any suggestion on how to determine if an instance of an entity is new or an update to an existing record?
Edit: I forgot to mention, these entities are POCOs built for code-first, so I don't have an EntityState property on the entity itself.
Yes, as the above answers state, you check the EntityState for the entity in the OSM.
However, keep in mind this only works for entities attached to the context/graph.
I'm currently working with detached entities (ASP.NET MVC), and because they are not attached to the graph, the EntityState is unchanged.
In this case, i am doing a precautionary call to the DB to grab the entity by the key. If nothing is returned, i do an Add, otherwise i use ApplyCurrentValues to override the values, then do .SaveChanges
I'm still wondering if this is the correct way, but thought i'd put it out there.
I'm using POCO's which have no change tracking, hence i have to do a bit more work.
Since there is no EntityState for the POCO, you have to manually call into the OSM:
var pocosInGraph = ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Modified)
After you Attach/Add, your POCO should be in that collection.
As i said though, if this is for a MVC application, your entities are detached on a HTTP POST, and thus the EntityState will still be unchanged.
In my case, i manually set the EntityState after Attaching:
ctx.Attach(poco);
ctx.ObjectStateManager.ChangeObjectState(poco, EntityState.Modified);
ctx.SaveChanges();
if (x.EntityState == System.Data.EntityState.Added)
//Add
else if (x.EntityState == System.Data.EntityState.Modified)
//Attach
for more info
http://msdn.microsoft.com/en-us/library/system.data.entitystate.aspx