Entity Framework Relationship Fix-up Failure - entity-framework

For the below code, the expected behavior of EF is to commit order and the orderItem with orderID=100 and as far as I know, this auto assignment of the field orderID is known as relatinship fix-up. So, we do not have to provide the orderID field for the orderItem to be added.
Entities ctx = new Entities();
Order order = new Order()
{
ID = 100,
name = "abc"
};
// ID field of OrderItem is auto-increment and the primary field
//OrderItem is a navigation property of Order through the orderID field
OrderItem orderItem = new OrderItem ()
{
name = "oi_abc"
};
order.OrderItems.Add(orderItem);
ctx.Orders.Add(order);
ctx.SaveChanges();
This piece of sample code works as expected. But the problem is, a similar piece of code in our project fails to do proper relationship fix-up. (I know it would be best if I could provide the whole code but it is impossible due to other complexities of the code and database and also due to our company policy.)
The code in our project simply sets orderID as 0 during commit.
Some important things to note:
We do not disable tracking changes of the context anywhere
Setting orderID manually works but it is not desired for us
If we have the ctx.Orders.Add(order); line right after creating the order object, then it work as expected.
Do you have any guesses as to why such a failure would happen, given the circumstances above?

Related

Updating entity without having the know primary key

Given the following code, how can I add an element to one of the properties of an entity without knowing its Id and retrieving it from the database?
public async Task BookInPersonVisitAsync(Guid propertyId, DateTime dateTime, CancellationToken token)
{
var entity = new OnBoardingProcessEntity{ ExternalId = propertyId };
DbContext.OnBoardingProcesses.Attach(entity);
entity.OnBoardingProcessVisits.Add(new OnBoardingProcessVisitEntity
{
DateTime = dateTime,
Occurred = false
});
await DbContext.SaveChangesAsync(token);
}
ExternalId is just a guid we use for external reference. This doesnt work cause it does not have the id set, but without hitting the database we cant have it.
With entity framework if you have to reference an entity (referencedEntity) from another entity (entity) you have to know referencedEntity.
Otherwise you can add just add the entity setting the referencedEntity to null.
To know the referencedEntity or you know the Id or you have to retrieve it in some ways (from the database).
In SQL (DML) if (and only if) ExternalId is a candidate key noy nullable you can insert the OnBoardingProcessVisit record with a single roundtrip but the insert statement will contain an inner query.
OnBoardingProcessVisit.OnBoardingProcess_Id = (
SELECT
Id
FROM
OnBoardingProcess
WHERE
ExternalId = #propertyId)
EDIT
No way to generate that query with EF. You can have a look to external components (free and not free, for example EntityFramework Extended but in this case I think that doesn't help).
In this case I probably would try to use standard entity framework features (so 1 roundtrip to retrieve the OnBoardingProcess from the ExternalId).
Then, if the roundtrip is too slow, run the SQL query directly on the database.
About performances (and database consistency) add a unique index on OnBoardingProcess.ExternalId (in every case).
Another suggestion if you decide for the roundtrip.
In your code, the entity will be a proxy. If you don't disable lazy load, using your code you will do one more roundtrip when you will access to property
entity.OnBoardingProcessVisits (in the statement entity.OnBoardingProcessVisits.Add).
So, in this case, disable lazy load or do the same using a different way.
The different way in your case is something like
var onBoardingProcessVisitEntity new OnBoardingProcessVisitEntity
{
DateTime = dateTime,
Occurred = false,
OnBoardingProcess = entity
});
DbContext.OnBoardingProcessVisits.Add(onBoardingProcessVisitEntity);
await DbContext.SaveChangesAsync(token);

Why does DbSet.Add change properties of other entities?

I have a rare case where the call of DbSet<T>.Add() changes some properties of other entities that are already in the DbSet<T>. Unfortunately, it happens very rarely, and the only evidence I have are some log files, so I have not yet been able to reproduce it locally.
The behavior is like this:
First, we load some entities from the DbSet using a LINQ query.
Then, some of these entities are changed. No SaveChanges() yet.
Now we add some entities by calling DbSet<T>.Add().
Some of the entities of step 2 are changed in step 3 (one foreign-key property of them is set to null).
Any idea? Is that something that can happen on a EF 6 Code-First model?
The only possibility I can think of is that the DbContext refreshes some data from the database, but we don't want it to do that at this point.
EDIT: The code is currently scattered with log statements, since we have been chasing this bug since weeks. These are the relevant code sections:
// parameter: List<Entry> entriesFromUser
var entriesFromDb = db.Entries
.Where(...)
.OrderBy(...)
.ToList();
var newEntries = MergeEntries(entriesFromDb, entriesFromUser);
var propertyBefore = entriesFromDb[0].MyForeignKeyId;
for (var i = 0; i < newEntries.Count; i++)
{
// make sure that the "new entry" is not a modified one
if (entriesFromDb.Contains(newEntries[i])
{
throw new Exception();
}
db.Entries.Add(newEntries[i]);
}
var propertyAfter = entriesFromDb[0].MyForeignKeyId;
Debug.Assert(propertyBefore == propertyAfter); // <=== fails sometimes
db.SaveChanges();
Please note that the changed foreign key is NOT on the entity being added to the DbSet. It's on an entity that comes from the database, but has been changed in the same transaction.
D'oh. Found the reason. Hope it helps someone else.
We are using Foreign Key Associations, which means that we have both the navigation property entry.MyForeignKey and the Foreign Key property entry.MyForeignKeyId, which has many advantages, but it also means you have to be careful when using sometimes this, sometimes that property.
Turns out we had the following assignment somewhere deep in the code, where all the data of one entry is copied to another one:
entry.MyForeignKeyId = otherEntry.MyForeignKeyId
entry.MyForeignKey = otherEntry.MyForeignKey
However, in many scenarios, you set a foreign key value to an entity's MyForeignKeyId but leave the property MyForeignKey null, because the parent entity is not loaded. This is fine as long as you don't assign null to the entity's MyForeignKey property, because it seems that EF would then set MyForeignKeyId to null too.
So it seems that after our code assigned null to MyForeignKey, the entity lingered in memory with a null MyForeignKey and a non-null MyForeignKeyId. As soon as the next DbSet command was executed (the Add() operation), the DbSet noticed that MyForeignKey has received a null assignment, so DbSet went on and assigned null to MyForeignKeyId too.

Attaching an related entity to new entity, without retrieving from database

I've just started working with Web API this week, and I'm struggling with something which I think should be quite simple, but haven't been able to find the answer for yet. Perhaps I'm searching using the wrong terms.
One of the calls to the API passes through a GUID. I need to create a new entity (using Entity Framework) and set one of the relations to this newly passed in GUID. This GUID is the ID of a different entity in the database.
I'm struggling to attach the entity via the relation without fetching the whole entity too.
For example,
public void DoWork(IList<Guid> userGuids)
{
Order order = new Order() // This is an entity
{
CreateDate = DateTime.Now,
CreatedBy = "Me",
Items = (from i in this.Model.Items
where i.Id == userGuid
select i).ToList<Item>();
}
Model.Orders.Add(order);
Model.SaveAll();
}
In the above, I have to do a database call to attach the Item entities to the Order. Is there not a way around this? Seems very redundant to retrieve the whole entity objects when I only require their IDs (which I already have anyway!)!
One solution is stub entities as asked here: Create new EF object with foreign key reference without loading whole rereference object
Link to the source blog referenced: http://blogs.msdn.com/b/alexj/archive/2009/06/19/tip-26-how-to-avoid-database-queries-using-stub-entities.aspx
Snip from the blog - to be applied to your situation:
Category category = new Category { ID = 5};
ctx.AttachTo(“Categories”,category);
Product product = new Product {
Name = “Bovril”,
Category = category
};
ctx.AddToProducts(product);
ctx.SaveChanges();
This way (in the example) the Product is saved without ever loading the Category object.

EF Saving changes suddenly stopped working

SaveChanges() on my EFv4 POCO context suddenly stopped working. It issues an error "Cannot insert NULL value into column 'postalcode' of table Clients"
Client entity contains a reference to PostCode (properties postalcode, postname) entity.
PostCode reference is not null and so aren't it's properties. My Client entity is referenced from the Document entity.
Here's the code
public void Add(Document instance)
{
// Firm reference
instance.Firm =
(from f in _ctx.Firms
where f.firm_id.Equals(AppState.FirmId)
select f).First();
// Client reference (lazy loading is in place and works)
if (instance.client_id != null)
instance.Client = (from c in _ctx.Clients
where c.client_id.Equals(instance.client_id)
select c).First();
instance.document_id = Guid.NewGuid();
_ctx.Documents.AddObject(instance);
_ctx.Documents.SaveChanges();
}
The thing is, AddObject() works but SaveChanges() fails. I've inspected the Document instance, Client reference and Client's PostCode reference all over and through, all the values are there (which also proves that lazyloading is in place and working) but save doesn't happen.
Looking for ideas what could I've missed..
Firstly, you get the error when you run SaveChanges because that is when it tries to write to the database. Before then it is just in memory.
The error is that postalcode does not allow nulls.
My guess is that in a previous version of the model nulls were allowed. So there are some records without postal codes.
Alternatively, when you retrieve the client the postalcodes are not being retrieved.
In both cases the result is that you are trying to save a postalcode with value null.

Entity Framework / EF4: Multiple inserts of related entities in a transactionscope

I have a similar problem.
I want to make two inserts in the same transactionscope. The objects are related and have a FK relationship between them, but for several reasons I do not want to connect them via the navigation property, but only by ID.
This is a simplification of what I what I want to accomplish:
Order o = new Order();
OrderDetails d = new OrderDetails();
new Repository().SaveNew(o, d);
class Repository{
void SaveNew(Order o, OrderDetails d){
using (TransactionScope transaction = new TransactionScope())
{
_context.Connection.Open();
// order
_context.Orders.ApplyChanges(o);
_context.SaveChanges();
// details
d.OrderID = o.ID;
_context.OrderDetails.ApplyChanges(d);
_context.SaveChanges(); <--- UpdateException
_context.Connection.Close();
transaction.Complete();
}
}
}
The problem is that I get an UpdateException because the FK evaluation fails. I tried to remove the FK relationship and running the exact same piece of code, and it worked fine, and both objects had the right properties set. So why does this approach fail? And how should this instead be done? Again, I do not want to attach the entites via their navigation properties.
Thank you!
I would leave the FK relationship in the database, but delete the AssociationSet and Association from the SSDL. The designer won't let you do this, you have to edit the XML manually.
I am using EF 4 btw.
Then use AddObject and SaveChanges in your SaveNew method to add the first (parent) object. Set the foreign key Property on the child and add it with AddObject and SaveChanges.
I do not have development environment running to test this, but what I think is happening is:
Assuming that the id is generated in the database. At the point when you save the order you do not know the ID.
Then the order ID of the order detail is set to the ID of the order, but the order was not reloaded from the database. I suspect that the value is 0.
When you try to save the order detail with FK of 0, you get an error.
Either save both at the same time so that EF does the work for you, or reload the order.