Entity Framework: Avoid multiple saves for circular references - c#

I'm getting this exception when calling SaveChanges on my EF ObjectContext:
Unable to determine a valid ordering for dependent operations. Dependencies may exist due to foreign key constraints
I think the problem is because I have a circular dependency on my DB model.
i.e.
Table Users
Id
ProfilePictureId
Table Pictures
Id
UserId
I'm creating a new user and setting the picture
var user = _db.Users.CreateObject();
var picture = _db.Pictures.CreateObject();
picture.User = user;
user.ProfilePicture = picture;
_db.SaveChanges();
But that throws the exception.
If I add an extra call to SaveChanges() after I set the picture's User It works just fine, I just want to avoid that double trip to the DB.
Any ideas of how to achieve this?
Thanks!

There is no way to avoid calling SaveChanges twice with your database design. You can't insert user with dependency to picture which is not inserted yet (FK will throw exception) and in the same time you can't insert picture with dependency to user which is not inserted yet (again FK will throw exception). That is not feature of EF that is feature of DB itself.
You also don't need to avoid multiple SaveChanges calls because of multiple roundtrips. EF doesn't have command batching so each insert, update or delete has its own roundtrip to database anyway.
If you want to call single SaveChanges you must change your database as follows:
Table Users
Id (PK, IDENTITY)
Table Pictures
Id (PK, FK to Users.Id, No IDENTITY)
This is native one-to-one relation where User is principal and Picture is dependent.

I would say that it's enough to make the assignment once (either setting user or picture) ie:
var user = _db.Users.CreateObject();
var picture = _db.Pictures.CreateObject();
user.ProfilePicture = picture;
_db.SaveChanges();
You are creating new entities at first two lines, and you 'link' them by user.ProfilePicture = picture. EF should handle the rest.
You don't need to setup relation from both sides.
Edit: What about this?
var user = _db.Users.CreateObject();
var picture = _db.Pictures.CreateObject();
picture.user = user;
user.ProfilePictureId = picture.Id;
_db.SaveChanges();

Related

Restore a deleted record with same Id as it has previously, when ID field (PK) is auto increment (MySQL with EF)

I'm trying to design a rollback changes system.
I'm tracking old record values when a record id modified or deleted and then I'm using that values to re-insert it when user choose to rollback it...
editions rollback are working as expected.
deletions are not... because when I attach the entity to DBContext it has the old Id but when I execute DbContext.SaveChanges(); the Id is replaced per a new one(Auto Incremented)
Here is the sample code:
var model = JsonConvert.DeserializeObject(oldValues.OldData, type);
DbSet mySet = this.Set(type);
mySet.Attach(model);
this.Entry(model).State = oldValues.AuditType == 0 ? EntityState.Modified : EntityState.Added;
base.SaveChanges();
into model.Id I can see the correct Id before save but after it EF changes it per a new one.
is there anyway to set temporally to keep the current Id instead of generate a new one?
I tried many solutions but none of them works.
Thanks
To disable the Auto Generated Id, you have to run this command in SQL:
SET IDENTITY_INSERT [<schema>].[<TableName>] ON;
Then you can insert whatever value you want for the PK, assuming it is still available. However, I don't think EF has a way to do this from code.
I'm not entirely sure this would work, but, in theory, you could run some ad-hoc SQL right before save-changes on a transaction you create and then see if EF will take it.
If that doesn't work, then try this:
ALTER TABLE theTableInQuestion AUTO_INCREMENT=1234
EIDT: Or try this approach:
How can I force entity framework to insert identity columns?

EF5 always sets id to 0

I'm adding objects to a database where the id isn't auto-autogenerated in the database due to me wanting to specify the id myself (It's stupid I know, just play along ^^)
So I'm using Entity Framework 5 to insert the data into the database, however, eventhou I set the id before saving it, when I look in the database it's always zero. Why is this and how do I fix it?
The code is like this:
public Profile Add()
{
Profile item = new Profile()
{
id = 1,
name = "Bob"
};
db.Entry(item).State = EntityState.Added;
db.SaveChanges();
return item;
}
EDIT
I tried with db.Profiles.Add(item). Same problem
1.- In your edmx designer, right click id column, select properties
2.- In StoreGeneratedPattern select None
As PhilipStuyck said, your model was out of sync with your database, whenever you change the database you must update the model (Right click empty space in edmx designer select Update model from database).
Check that your database and your model are actually the same.
If you created your model from an existing database and then changed the database then your model is out of sync. This can also happen with code first of course.
Bottom line is that your sql will do an insert without a value for id, because EF thinks your id is going to come from the database. If you inspect the sql you will see an insert without id being provided followed with a select to get the value for id.
Conclusion is that your model and db are not the same.
Right click the designer and do update model from database.
You might have to remove your id column, or the table to begin with, EF will correct everything
You need to specify the table that you are adding it to.
so for you that would be db.NAMEOFTABLE.add(item)
normally you don't have to change the entity state to added.
Atleast I didn't have to do that in my solution.
You need to add the entity to the DbSet that represents your profiles table... something like this...
public Profile Add()
{
Profile item = db.Profiles.Create();
item.Name = "Bob";
db.Profiles.Add(item);
db.SaveChanges();
return item;
}
I prefer using the Create method however it makes no difference.
By default Id property is treated as Primary Key and as Identity by Entity Framework. So it just ignores property value, while generating sql for insert command. You should specify metadata explicitly: add [DatabaseGenerated(DatabaseGeneratedOption.None)] attribute or use method HasDatabaseGeneratedOption from FluentApi.

Best practice for archiving an entity in EF code first

I'm trying to archiving an entity of a table. There are couple of ways to do this. One of them is to create IsArchived column and set it to true when an entity is deleted or put into history. One of the disadvantage of this design will make specified table so heavy.
Another way to do this is to create the duplication of the class of specified entity to be logged, make another table, and adding it to log table with the help of AutoMapper. In this case i need lots of duplicate classes of entities which needed to be archived.
Is there any other solutions for archiving specified entities?
The best way would be to add a nullable ArchivedTimeStamp column to the table. This way, it is possible to tell if the row was archived or not, and if so, when it was archived.
If you are worried about the table size, you can partition the table and automatically move the archived rows onto a secondary / slower physical disk. You can even partition it in such a way that only rows that was, let say, archived over a year ago, must be moved to the secondary partition.
More info on on SQL archiving using partitioning can be found on http://www.mssqltips.com/sqlservertip/2780/archiving-sql-server-data-using-partitioning/
You could have more than one database, with the same schema. You can then open a couple contexts, one to each database, using a different connection string. Query one, attach the entities to the other, and save.
I've never done this, but it should work. You might run into trouble since the entities are going to be attached to the source context and cannot be attached to the destination, but there are ways to unattach and reattach the entities.
I have implemented a soft delete for the purposes of undo. My answer shows how to overcome some of the problems normally associated with soft deletes - i.e. joins and indexes. It suits my purposes well. However, if it was used for archiving then the tables would grow forever.
Your other idea is to create duplicate classes and use automapper. That sounds like a lot of extra coding.
I think you could create a database with the same schema - except, perhaps, the primary keys would not be database generated, and foreign keys not enforced. Then override the delete so that the data is copied over.
Something like this:
public override int SaveChanges()
{
foreach (var entry in ChangeTracker.Entries()
.Where(p => p.State == EntityState.Deleted
&& p.Entity is ModelBase))//I have a base class for entities with a single
//"ID" property - all my entities derive from this
CustomDelete(entry);
return base.SaveChanges();
}
private void CustomDelete(DbEntityEntry entry)
{
var e = entry.Entity as ModelBase;
string tableName = GetTableName(e.GetType());
string sql = String.Format(#"INSERT INTO archive.{0} SELECT * FROM {0} WHERE ID = #id;
DELETE FROM {0} WHERE ID = #id", tableName);
Database.ExecuteSqlCommand(
sql
, new SqlParameter("id", e.ID));
entry.State = EntityState.Detached;
}
Note that in EF6 you could also override the delete by altering the sql in the migration file when mapping to stored procedures is used

EF 4.1 code first - How to update/delete many to many join table entries automatically

I have 2 entities, let's say, Trip and Activity. The relationship between them is many to many so a join table is created automatically by EF.
Entity Trip attributes:
-Id (PK) Generated by database
-Name
-Description
-Property1
-Property2
-Property3
Entity Activity attributes (this entity contains fixed records -read only-, no records are inserted here on performing inserts):
-Id (PK) Generated by database
-Name
-Description
-Cost
Join table contains 2 columns, that is, the IDs of the above entities, that are primary and foreign keys at the same time.
I have no problems inserting entries which automatically EF creates join table TripActivities and add entries successfully to it. Also entries are added successfully to entity Trip and it leaves unchanged entity Activity.
My problem is on updating entries, for example, - suppose user can modify information related to a trip from the GUI - so I take all the info from this GUI and I perform the following steps to update the existing trip:
Trip trip = Context.Trips.Find(id); // Search for the appropriate trip to update from Id
trip.Name = ObtainNameFromGUI();
trip.Description = ObtainDescriptionFromGUI();
trip.Property1 = ObtainProperty1FromGUI();
trip.Property2 = ObtainProperty2FromGUI();
trip.Property3 = ObtainProperty3FromGUI();
trip.Activities = new List<Activity>();
// From the GUI user selects from a checkbox list the activities associated to the trip
// So we read its Ids and from those ids we fetch from database the activities to obtain
// the info related to each activity selected in the GUI. This is all done inside the
// below method.
List<Activity> activities = this.ObtainActivitiesSelectedFromGUI();
// If no activites selected (=null) I want EF automatically deletes the entries in the
// joined table for this trip. And of course, if there are activities selected, EF
// should update the respectives entries in the joined table for this trip with the new
// ones.
if (activites != null)
{
activities.ForEach(a =>
{
trip.Activities.Add(a);
});
}
context.Trips.Add(trip);
context.SaveChanges();<br><br>
By doing this I want EF updates all the entities related (except Activity as it has fixed entries, must be kept unchanged), that is, Trip and the joined table automatically but it does not work: a new trip is created and more entries in the joined table (The only thing that is working is that entity Activity is kept unchanged as I want).
How to achieve this? I have spent a lot of hours trying to do this but without success...
Thanks in advance.
EDIT:
I have removed line:
context.Trips.Add(trip);
Now the results are:
-Entity Trip is correctly updated, no new records added which is Ok.
-Entity Activity is kept unchanged which is Ok.
-Join table: The old records for current trip being updated are not updated, instead new records are inserted for the current trip which is not correct.
I have used a different approach for similar scenario that I faced, which works well with Detached Entities. What I ended up was finding out which entities were added and which ones deleted by comparing GUI(detached entity) values to the database values. Here is the sample code that I have used. The entities in play are RelayConfig and StandardContact which have many to many relationship
public void Update(RelayConfig relayConfig, List<StandardContact> exposedContacts) {
RelayConfig dbRelayConfig = context.RelayConfigs.Include(r => r.StandardContacts)
.Where(r => r.Id == relayConfig.Id).SingleOrDefault();
context.Entry<RelayConfig> (dbRelayConfig).CurrentValues.SetValues(relayConfig);
List<StandardContact> addedExposedContacts =
exposedContacts.Where(c1 => !dbRelayConfig.StandardContacts.Any(c2 => c1.Id == c2.Id)).ToList();
List<StandardContact> deletedExposedContacts =
dbRelayConfig.StandardContacts.Where(c1 => !exposedContacts.Any(c2 => c2.Id == c1.Id)).ToList();
StandardContact dbExposedContact = null;
addedExposedContacts.ForEach(exposedContact => {
dbExposedContact = context.StandardContacts.SingleOrDefault(sc => sc.Id == exposedContact.Id);
dbRelayConfig.StandardContacts.Add(dbExposedContact);
});
deletedExposedContacts.ForEach(exposedContact => { dbRelayConfig.StandardContacts.Remove(exposedContact);});
You will use something like this. Assuming that you will get the related objects from the UI and just you are going to update the same in the database, some thing like the following will work.
context.Products.Attach(product);
context.ObjectStateManager.ChangeObjectState(product, System.Data.EntityState.Modified);
context.ObjectStateManager.ChangeObjectState(product.ProductDescription, System.Data.EntityState.Modified);
context.ObjectStateManager.ChangeObjectState(product.ProductModel, System.Data.EntityState.Modified);
context.SaveChanges();
As you may see here, we are setting the EntityState as Modified which hints EF to perform update for the related tables too.
Please post back your queries or any issues that you may encounter in this implementation.

Database doubly connected relationship inserting problem

I have two tables Plants and Information. For every plant there are many information, but for each plant there is a single MainInformation. So there is a one-to-many relationship and a one-to-one relationship between the two. The Information table has a PlantID and the Plants table has a MainInformationID. I want both fields in both tables not to be nulls. But now you can't insert either of the two records into their tables because each one requires their fields not be null, meaning they need the other record to be created first in order to create themselves. Perhaps this is not a good database design and something should be changed? (I am new to databases and entity framework)
I tried inserting into the database itself manually but I cant do it. I also tried this code with EntityFramework.
using (var context = new MyEntities())
{
var p = new Plant()
{
LatinName = "latinNameTest",
LocalName = "localNameTest",
CycleTime = 500
};
var i = new Information()
{
ShortDescription = "ShortDesc",
LongDescription = "LongDesc"
};
p.MainInformation = i;
i.Plant = p;
context.AddToPlants(p);
context.AddToInformation(i);
context.SaveChanges();
}
One of
The 1-1 FK column has to be NULL
The FK has to be disabled to allow parent insert before child
You have a single dummy Information row that is used by default in FL column
SQL Server does not allow deferred constraint checking without "code change" rights so even wrapping in a transaction won't work
Sounds like an EAV schema, which has other problems
You need to change the tables to allow for null. There is no other way to do this.
You may want to look at database transactions and how to use them with the Entity Framework. You can wrap both INSERTS into a single db transaction so the only results are both of them go in or neither go in.
Here is a link for transactions using EF. I didn't read through it but it seems to talk about them enough to get you started.

Categories