Entity framework performing an Insert, when it should be doing an Update - entity-framework

I am having a real issue with the EF v1. I have quite a big EDMX with maybe 50 entities mapped, but this one entity is causing me grief.
The entity has mappings to other entities which in effect are reference tables, but for some reason it is trying to do an insert and not just update itself.
Here is a fragment of my code:
using (var context = new someEntities()) {
var studentCourseJoin =
context.StudentCourseJoinSet.Where(o => o.Code == scjCode).First();
studentCourseJoin.EntryStatus = new EntryStatus { Code = viewModel.StudentDetails.EntryStatusCode };
studentCourseJoin.ParentalInHigherEducation = new ParentalInHigherEducation { Code = viewModel.StudentDetails.ParentalInHigherEducationCode };
studentCourseJoin.School = new School { Code = viewModel.StudentDetails.SchoolCode };
studentCourseJoin.Institution = new Institution { Code = viewModel.StudentDetails.InstitutionCode };
studentCourseJoin.LastSchoolEndYear = viewModel.StudentDetails.LastSchoolEndYear;
studentCourseJoin.LastInstitutionEndYear = viewModel.StudentDetails.LastInstitutionEndYear;
// Blows up here trying to do an insert on the studentCourseJoin.Institution.
// But if I removed this one, then it will blow up on another one.
context.SaveChanges(true);
}
If anyone has ANY ideas please, they would help a lot.

Try adding those lines before calling SaveChanges:
ObjectStateEntry entry = context.ObjectStateManager.GetObjectStateEntry(studentCourseJoin);
entry.ChangeState(EntityState.Modified);
Update:
Try this for Institution instead:
studentCourseJoin.Institution = context.Institutions.FirstOrDefault(i => i.Code == viewModel.StudentDetails.InstitutionCode);

Related

Entity Framework 6: is it possible to update specific object property without getting the whole object?

I have an object with several really large string properties. In addition, it has a simple timestamp property.
What I trying to achieve is to update only timestamp property without getting the whole huge object to the server.
Eventually, I would like to use EF and to do in the most performant way something equivalent to this:
update [...]
set [...] = [...]
where [...]
Using the following, you can update a single column:
var yourEntity = new YourEntity() { Id = id, DateProp = dateTime };
using (var db = new MyEfContextName())
{
db.YourEntities.Attach(yourEntity);
db.Entry(yourEntity).Property(x => x.DateProp).IsModified = true;
db.SaveChanges();
}
OK, I managed to handle this. The solution is the same as proposed by Seany84, with the only addition of disabling validation, in order to overcome issue with required fields. Basically, I had to add the following line just before 'SaveChanges():
db.Configuration.ValidateOnSaveEnabled = false;
So, the complete solution is:
var yourEntity = new YourEntity() { Id = id, DateProp = dateTime };
using (var db = new MyEfContextName())
{
db.YourEntities.Attach(yourEntity);
db.Entry(yourEntity).Property(x => x.DateProp).IsModified = true;
db.Configuration.ValidateOnSaveEnabled = false;
db.SaveChanges();
}

Entities may have been modified or deleted since entities were loaded. Error during transaction

During the execution of the following piece of code, I get the message
Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. Refresh ObjectStateManager entries.
And there are loads of occurrences of that in here, but I didn't find a solution to my problem.
This only happens to me because I'm running _db.SaveChanges() two times inside one transaction (_db is my database context object), and I HAVE to do so because I need the generated ID that it gives me in order to proceed with the code.
If anyone can help me, I'd appreciate it. Also, if you know a way to proceed without the two _db.SaveChanges() or with a different way of approaching the transaction, I welcome you to show it.
using (var trans = _db.Database.BeginTransaction())
{
try
{
var f = cbxFornecedor.SelectedItem as Fornecedor;
var c = new Compra
{
CompraId = compra.CompraId,
DataCompra = dtpDataCompra.Value,
ListaProdutos = new List<ListaProdutos>(),
Fornecedor = f,
Referencia = tbxReferencia.Text,
Situacao = rbtEntregue.Checked
};
_db.Compras.Add(c);
_db.SaveChanges();
foreach (var cada in _itens)
c.ListaProdutos.Add(new ListaProdutos
{
Compra = c,
CompraId = c.CompraId,
Produto = cada.ProdutoClasse,
ProdutoId = cada.ProdutoClasse.ProdutoId,
Valor = cada.ValorTotal,
Quantidade = cada.Quantidade,
});
foreach (var cada in c.ListaProdutos)
if (_db.ListaProdutos.Find(cada.CompraId, cada.ProdutoId) != null)
_db.Entry(cada).State = EntityState.Modified;
else
_db.ListaProdutos.Add(cada);
_db.Entry(c).State = EntityState.Modified;
_db.SaveChanges();
trans.Commit();
}
catch (Exception ex)
{
trans.Rollback();
MessageBox.Show(this, ex.Message, #"Erro", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
And, just to be clear, I know that my problem is with that fact that the transaction is there. I tried removing it and it worked. Also, if I take out the second _db.SaveChanges(), it works too.
The exception is only thrown in the second _db.SaveChanges(), and only if it is surrounded by the transaction block.
It is not direct answer to your question, but it may be helpful.
You do not need to specify Compra and CompraId for new ListaProdutos because EF smart enough to figure it out. Also it looks like that you do not need _db.ListaProdutos.Find(cada.CompraId, cada.ProdutoId) != null check because cada.CompraId - is new Id and it can not be in DB at this moment. Also you do not need _db.ListaProdutos.Add(cada); because EF already new that you are adding this records. Can you try next code?
var f = cbxFornecedor.SelectedItem as Fornecedor;
var c = new Compra
{
DataCompra = dtpDataCompra.Value,
ListaProdutos = new List<ListaProdutos>(),
Fornecedor = f,
Referencia = tbxReferencia.Text,
Situacao = rbtEntregue.Checked
};
_db.Compras.Add(c);
foreach (var cada in _itens)
c.ListaProdutos.Add(new ListaProdutos
{
Produto = cada.ProdutoClasse,
ProdutoId = cada.ProdutoClasse.ProdutoId,
Valor = cada.ValorTotal,
Quantidade = cada.Quantidade,
});
_db.SaveChanges();

Entity Framework dbset not finding added entity

I am having an issue understanding why when adding a new entity to a DbSet of ObjectContext, that entity is not found will looking it up again.
using (var db = new SmartrailDB())
{
var cart01 = db.Carts.SingleOrDefault(x => x.Number == 0);
if (cart01 == null)
{
cart01 = new Cart { Number = 0 };
db.Carts.Add(cart01);
}
var cart02 = db.Carts.SingleOrDefault(x => x.Number == 0); // Should find the cart I just added - right?
Assert.IsNotNull(cart02); // Fails because cart02 does not exist in the db.Carts collection
}
Is anyone able to tell me what I am doing wrong here?
Also late on a Friday here so brain half asleep now.
You have to update your context before you try to access the entity. Just do:
db.SaveChanges(); right after db.Cart.Add(cart01);

.Net Entity Framework SaveChanges is adding without add method

I'm new to the entity framework and I'm really confused about how savechanges works. There's probably a lot of code in my example which could be improved, but here's the problem I'm having.
The user enters a bunch of picks. I make sure the user hasn't already entered those picks.
Then I add the picks to the database.
var db = new myModel()
var predictionArray = ticker.Substring(1).Split(','); // Get rid of the initial comma.
var user = Membership.GetUser();
var userId = Convert.ToInt32(user.ProviderUserKey);
// Get the member with all his predictions for today.
var memberQuery = (from member in db.Members
where member.user_id == userId
select new
{
member,
predictions = from p in member.Predictions
where p.start_date == null
select p
}).First();
// Load all the company ids.
foreach (var prediction in memberQuery.predictions)
{
prediction.CompanyReference.Load();
}
var picks = from prediction in predictionArray
let data = prediction.Split(':')
let companyTicker = data[0]
where !(from i in memberQuery.predictions
select i.Company.ticker).Contains(companyTicker)
select new Prediction
{
Member = memberQuery.member,
Company = db.Companies.Where(c => c.ticker == companyTicker).First(),
is_up = data[1] == "up", // This turns up and down into true and false.
};
// Save the records to the database.
// HERE'S THE PART I DON'T UNDERSTAND.
// This saves the records, even though I don't have db.AddToPredictions(pick)
foreach (var pick in picks)
{
db.SaveChanges();
}
// This does not save records when the db.SaveChanges outside of a loop of picks.
db.SaveChanges();
foreach (var pick in picks)
{
}
// This saves records, but it will insert all the picks exactly once no matter how many picks you have.
//The fact you're skipping a pick makes no difference in what gets inserted.
var counter = 1;
foreach (var pick in picks)
{
if (counter == 2)
{
db.SaveChanges();
}
counter++;
}
I've tested and the SaveChanges doesn't even have to be in the loop.
The below code works, too.
foreach (var pick in picks)
{
break;
}
db.SaveChanges()
There's obviously something going on with the context I don't understand. I'm guessing I've somehow loaded my new picks as pending changes, but even if that's true I don't understand I have to loop over them to save changes.
Can someone explain this to me?
Here's updated working code based on Craig's responses:
1) Remove the Type then loop over the results and populate new objects.
var picks = (from prediction in predictionArray
let data = prediction.Split(':')
let companyTicker = data[0]
where !(from i in memberQuery.predictions
select i.Company.ticker).Contains(companyTicker)
select new //NO TYPE HERE
{
Member = memberQuery.member,
Company = db.Companies.Where(c => c.ticker == companyTicker).First(),
is_up = data[1] == "up", // This turns up and down into true and false.
}).ToList();
foreach (var prediction in picks)
{
if (includePrediction)
{
var p = new Prediction{
Member = prediction.Member,
Company = prediction.Company,
is_up = prediction.is_up
};
db.AddToPredictions(p);
}
}
2) Or if I don't want the predictions to be saved, I can detach the predictions.
foreach (var prediction in picks) {
if (excludePrediction)
{
db.Detach(prediction)
}
}
The reason is here:
select new Prediction
{
Member = memberQuery.member,
These lines will (once the IEnumerable is iterated; LINQ is lazy) :
Instantiate a new Prediction
Associate that Prediction with an existing Member, *which is attached to db.
Associating an instance of an entity with an attached entity automatically adds that entity to the context of the associated, attached entity.
So as soon as you start iterating over predictionArray, the code above executes and you have a new entity in your context.

EntityReference has an EntityKey property value that does not match?

I am attempting to add some entities that I have created. When I try and add the entity in question to the set (see code below) I get the following error:
"The object could not be added or attached because its EntityReference has an EntityKey property value that does not match the EntityKey for this object."
I can't tell what entitykey it's referring to though. Here is the code, there is probably a much better way to pull this off as well:
public Internship CreateInternship(Internship internshipToCreate)
{
try
{
Contact contactToCreate = new Contact();
contactToCreate.Fax = internshipToCreate.contacts.Fax;
contactToCreate.Extension = internshipToCreate.contacts.Extension;
contactToCreate.FirstName = internshipToCreate.contacts.FirstName;
contactToCreate.MiddleName = internshipToCreate.contacts.MiddleName;
contactToCreate.LastName = internshipToCreate.contacts.LastName;
contactToCreate.PhoneNumber = internshipToCreate.contacts.PhoneNumber;
contactToCreate.StreetAddress = internshipToCreate.contacts.StreetAddress;
contactToCreate.PostalCode = internshipToCreate.contacts.PostalCode;
contactToCreate.ContactEmail = internshipToCreate.contacts.ContactEmail;
contactToCreate.statesReference.EntityKey =
new EntityKey("InternshipEntities.StateSet", "ID", internshipToCreate.contacts.states.ID);
contactToCreate.countriesReference.EntityKey =
new EntityKey("InternshipEntities.CountrySet", "ID", internshipToCreate.contacts.countries.ID);
_internshipEntities.AddToContactSet(contactToCreate);
_internshipEntities.SaveChanges();
try
{
Availability availabilityToCreate = new Availability();
availabilityToCreate.StartDate = internshipToCreate.availability.StartDate;
availabilityToCreate.EndDate = internshipToCreate.availability.EndDate;
availabilityToCreate.Negotiable = internshipToCreate.availability.Negotiable;
_internshipEntities.AddToAvailabilitySet(availabilityToCreate);
_internshipEntities.SaveChanges();
try
{
internshipToCreate.contactsReference.EntityKey =
new EntityKey("InternshipEntities.ContactSet", "ID", contactToCreate.ID);
internshipToCreate.availabilityReference.EntityKey =
new EntityKey("InternshipEntities.AvailabilitySet", "ID", availabilityToCreate.ID);
internshipToCreate.classificationsReference.EntityKey =
new EntityKey("InternshipEntities.ClassificationSet", "ID", internshipToCreate.classifications.ID);
internshipToCreate.educationReference.EntityKey =
new EntityKey("InternshipEntities.EducationSet", "ID", internshipToCreate.education.ID);
_internshipEntities.AddToInternshipSet(internshipToCreate); //exception here
_internshipEntities.SaveChanges();
return internshipToCreate;
}
catch(Exception e)
{
throw e;
}
}
catch(Exception e)
{
throw e;
}
}
catch(Exception e)
{
throw e;
}
}
There is no other information given besides the error when I trace through so I'm not even sure which Key is the issue.
EDIT: Here is the version that ended up working:
using (TransactionScope scope = new TransactionScope())
{
try
{
Contact contactToCreate = new Contact();
Availability availabilityToCreate = new Availability();
Internship i = new Internship();
// Set the contact entity values;
contactToCreate.Fax = internshipToCreate.contacts.Fax;
//...
//ommited for brevity
//...
contactToCreate.ContactEmail = internshipToCreate.contacts.ContactEmail;
// Set the contact entity references to existing tables
contactToCreate.statesReference.EntityKey =
new EntityKey("InternshipEntities.StateSet", "ID", internshipToCreate.contacts.states.ID);
contactToCreate.countriesReference.EntityKey =
new EntityKey("InternshipEntities.CountrySet", "ID", internshipToCreate.contacts.countries.ID);
// Add contact
_internshipEntities.AddToContactSet(contactToCreate);
// Set the availability entity values;
availabilityToCreate.StartDate = internshipToCreate.availability.StartDate;
availabilityToCreate.EndDate = internshipToCreate.availability.EndDate;
availabilityToCreate.Negotiable = internshipToCreate.availability.Negotiable;
// Add availability
_internshipEntities.AddToAvailabilitySet(availabilityToCreate);
//Add contact and availability entities to new internship entity
i.contacts = contactToCreate;
i.availability = availabilityToCreate;
// Set internship entity values;
i.UserID = internshipToCreate.UserID;
//...
//ommited for brevity
//...
i.Created = DateTime.Now;
// Set the internship entity references to existing tables
i.classificationsReference.EntityKey =
new EntityKey("InternshipEntities.ClassificationSet", "ID", internshipToCreate.classifications.ID);
i.educationReference.EntityKey =
new EntityKey("InternshipEntities.EducationSet", "ID", internshipToCreate.education.ID);
// Add internship and save
_internshipEntities.AddToInternshipSet(i);
_internshipEntities.SaveChanges();
//commit transaction
scope.Complete();
return internshipToCreate;
}
catch (Exception e)
{
throw e;
}
}
Hallo,
although I'm not sure what the problem is I have a suggestion. The Internship object that you are passing into method (internshipToCreate) is used to transfer values to other entities (Contact, Availability) that you instantiated inside of the method, and their creation works just fine.
Maybe you should try to do the same with Internship? Create new Internship object and set all values you have by taking them from internshipToCreate object, and than that newly created object pass to the _internshipEntities.AddToInternshipSet method.
It is possible that you've set some values on internshipToCreate object that you needed for other purposes, and that some of those is actually causing the exception.
And, I don't know what you business logic is, but it would be better if you put all under one transaction, because like this it may happen that first two entities are created, and third one not.
This code isn't making a lot of sense to me. In two cases, you're going through an EntityKey when you could just assign an object reference. I.e, change this:
internshipToCreate.contactsReference.EntityKey =
new EntityKey("InternshipEntities.ContactSet", "ID", contactToCreate.ID);
internshipToCreate.availabilityReference.EntityKey =
new EntityKey("InternshipEntities.AvailabilitySet", "ID", availabilityToCreate.ID);
...to:
internshipToCreate.contacts = contactToCreate;
internshipToCreate.availability = availabilityToCreate;
In the other two cases you seem to be attempting to assign the ID of the object which is already there. These two lines, even if successful, it seems to me, would do nothing:
internshipToCreate.classificationsReference.EntityKey =
new EntityKey("InternshipEntities.ClassificationSet", "ID", internshipToCreate.classifications.ID);
internshipToCreate.educationReference.EntityKey =
new EntityKey("InternshipEntities.EducationSet", "ID", internshipToCreate.education.ID);
So you can just get rid of them.
What happens when you make these two changes?