Entity Framework 4 Delete call not working - c#

I'm trying to delete a record from my table using EF, and nothing is happening. The code executes with no errors, but the record just sticks around in the DB. I have nearly identical code elsewhere that's working.
using (var DB = new PTNWebConfigurationModel.PTNWebConfigurationEntities())
{
var account = DB.Accounts.Where(a => a.LoginID == loginID).FirstOrDefault();
//Load existing session for the Account, otherwise create a new one.
if (!LoadExistingSession(ip, DB, account))
{
CreateNewSession(ip, DB, account);
}
AccountsSession sessionsToDelete = DB.AccountsSessions.Where(a => a.RemoteIP == ip && a.AccountID == 1).FirstOrDefault();
if (sessionsToDelete != null)
{
DB.DeleteObject(sessionsToDelete);
DB.SaveChanges();
}
}
I've also tried it with these options:
DB.DeleteObject(sessionsToDelete);
DB.SaveChanges(System.Data.Objects.SaveOptions.DetectChangesBeforeSave);
DB.AcceptAllChanges();
I also started with no using block and just and instantiated entity object, but that didn't work either.

I love issues like this.
Most of the times that I have seen an ORM not execute a command (and without error) is due to changes to the underlying data tables that aren't represented in the generated classes for that table.
You might refresh your schema / class model and try again.

Carrying on from Chris' answer the other thing I've found in the past is if you are doing EF database first modelling then you may have created the foreign keys linking two associated tables but the key hasn't been set as delete cascade in the relationship. This can cause EF to be a little difficult. So check any FKs in your db. Hope that helps.

Related

EntityFramework core - Update a collection of data without selecting the entities

How would you Upsert without select? the upsert would be a collection of entities received by a method which contains DTOs that may not be available in the database so you can NOT use attach range for example.
One way theoretically is to load the ExistingData partially with a select like dbContext.People.Where(x => x exists in requested collection).Select(x => new Person { Id = x.Id, State = x.State }).ToList() which just loads a part of the entity and not the heavy parts. But here if you update one of these returned entityItems from this collection it will not update because of the new Person its not tracking it and you also cannot say dbContext.Entry<Person>(person).State = Modified because it will throw an error and will tell you that ef core is already "Tracking" it.
So what to do.
One way would be to detach all of them from the ChangeTracker and then do the state change and it will do the update but not just on one field even if you say dbContext.Entry<Person>(person).Property(x => x.State).Modified = true. It will overwrite every fields that you haven't read from the database to their default value and it will make a mess in the database.
The other way would be to read the ChangeTracker entries and update them but it will also overwrite and it will consider like everything is chanaged.
So techinically I don't know how ef core can create the following SQL,
update People set state = 'Approved' where state != 'Approved'
without updating anything else. or loading the person first completely.
The reason for not loading your data is that you may want to update like 14000 records and those records are really heavy to load because they contain byte[] and have images stored on them for example.
BTW the lack of friendly documentation on EFCore is a disaster compare to Laravel. Recently it has cost us the loss of a huge amount of data.
btw, the examples like the code below will NOT work for us because they are updating one field which they know that it exists in database. But we are trying to upsert a collection which some of those DTOs may not be available in the database.
try
{
using (var db = new dbContext())
{
// Create new stub with correct id and attach to context.
var entity = new myEntity { PageID = pageid };
db.Pages.Attach(entity);
// Now the entity is being tracked by EF, update required properties.
entity.Title = "new title";
entity.Url = "new-url";
// EF knows only to update the propeties specified above.
db.SaveChanges();
}
}
catch (DataException)
{
// process exception
}
Edit: The used ef core version is #3.1.9
Fantastic, I found the solution (You need to also take care about your unit tests).
Entityframework is actually working fine it can be just a lack of experience which I'm documenting here in case anyone else got into the same issue.
Consider that we have an entity for Person which has a profile picture saved as Blob on it which causes that if you do something like the following for let's say 20k people the query goes slow even when you've tried to have enough correct index on your table.
You want to do this query to update these entities based on a request.
var entityIdsToUpdate = request.PeopleDtos.Select(p => p.Id);
var people = dbContext.People.Where(x => entityIdsToUpdate.Contains(x.Id)).ToList();
This is fine and it works perfectly, you will get the People collection and then you can update them based on the given data.
In these kind of updates you normally will not need to update images even if you do, then you need to increase the `TimeOut1 property on your client but for our case we did not need to update the images.
So the above code will change to this.
var entityIdsToUpdate = request.PeopleDtos.Select(p => p.Id);
var people = dbContext.People
.Select(p => new Person {
Id = p.Id,
Firstname = p.Firstname,
Lastname = p.Lastname,
//But no images to load
})
.Where(p => entityIdsToUpdate.Contains(p.Id)).ToList();
But then with this approach, EntityFramework will lose the track of your entities.
So you need to attach it like this and I will tell you how NOT to attach it.
This is the correct way for a collection
dbContext.People.AttachRange(people); //These are the people you've already queried
Now DO NOT do this, you may want to do this because you get an error from the first one from EntityFramework which says the entity is already being tracked, trust it because it already is. I will explain after the code.
//Do not do this
foreach(var entry in dbContext.ChangeTracker.Entries())
{
entry.State = EntityState.Detached;
}
//and then on updating a record you may write the following to attach it back
dbContext.Entry(Person).State = EntityState.Modified;
The above code will cause EntityFramework not to follow the changes on the entities anymore and by the last line you will tell it literally everything edited or not edited is changed and will cause you to LOSE your unedited properties like the "image".
Note: Now what can u do by mistake that even messes up the correct approach.
Well since you are not loading your whole entity, you may assume that it is still fine to assign values to the unloaded ones even if the value is not different than the one in the database. This causes entity framework to assume that something is changed and if you are setting a ModifiedOn on your records it will change it for no good reason.
And now about testing:
While you test, you may get something out from database and create a dto from that and pass the dto with the same dbContext to your SystemUnderTest the attach method will throw an error here which says this entity is already bein tracked because of that call in your test method. The best way would be create a new dbContext for each process and dispose them after you are done with them.
BTW in testing it may happen that with the same dbContext you update an entity and after the test you want to fetch if from the database. Please take note that this one which is returning to you is the "Cached" one by EntityFramework and if you have fetched it in the first place not completely like just with Select(x => ) then you will get some fields as null or default value.
In this case you should do DbContext.Entry(YOUR_ENTRY).Reload().
It is a really complete answer it may not directly be related to the question but all of the things mentioned above if you don't notice them may cause a disaster.

Entity Framework updating many to many

I am trying to find the suitable form of updating a many to many relationship but i am find some issues on it.
The application is an asp.net mvc with simple injector(set up per context)
I have an entity People which has an IEnumerable and also i have a entity Team which has an IEnumerable.
The People entity has some other fields like Description, Email, etc and in its View, there are some check boxes so the user can choose the Teams.
I had tried to search on the net for the best approach for updating a many to many relationship and all that i found was deleting everything in the third table that is created and then add the Teams again.
Under is what i am trying to do, but i am getting pk's already exists. I know it is happening because firstly i load the People entity with Find method(to remove the list of Teams inside a foreach) and after i try to Attach(when the error happens) the modified object to set it's State to Modified.
public override void Modify(People obj)
{
var ppl = SearchById(obj.Id);
if (ppl.Teams.Count > 0)
{
foreach (var team in ppl.Teams.ToList())
{
ppl.Teams.Remove(team);
}
}
var entry = lpcContext.Entry(obj);
if (lpcContext.Entry(obj).State == EntityState.Detached)
dbSet.Attach(obj);
entry.State = EntityState.Modified;
}
To air it out some things, i am using the Unit Of Work pattern, so i SaveChanges later.
Are there any other approach or i have to remove the Teams one by one, SaveChanges and after that, update the object and SaveChanges again?
Unfortunately, working with detached entities isnt that straight forward in EF (yet). Attach() in EF will work for connected entities only. That means if you load an object from DB, pass it on to a view (or page is asp.net). When you read the object back from that view/page, EF will not be tracking that object anymore. If you now try to use Attach(), you will get an error that the key already exists in the DBContext. To workaround this, you need to find the entry and make changes to the entity using SetValues(). Something like this:
public virtual void Update(T entity)
{
DbEntityEntry dbEntityEntry = DbContext.Entry(entity);
if (dbEntityEntry.State == EntityState.Detached)
{
var pkey = _dbset.Create().GetType().GetProperty("Id").GetValue(entity);//assuming Id is the key column
var set = DbContext.Set<T>();
T attachedEntity = set.Find(pkey);
if (attachedEntity != null)
{
var attachedEntry = DbContext.Entry(attachedEntity);
attachedEntry.CurrentValues.SetValues(entity);
}
}
}
Please note that this will ignore any nested objects. Hence, you should make DB trip, and compare the object returned from DB to find out if you should invoke Add, Update or Delete on each child object. This is the best workaround I could find when working with disconnected objects in EF. I guess nHibernate doesnt have this bug. Last I read about this, Microsoft was going to work on this after EF 6.x. So, we'll have to wait for this, I guess. Please go through the below article to understand the issue (and possible solutions) in length:
http://blog.maskalik.com/entity-framework/2013/12/23/entity-framework-updating-database-from-detached-objects/
To talk about your specfic scenario, you should make a DB hit and find out if any new teams were selected or some existing team was dropped and call add or delete as appropriate by comparing the Team collection of People object returned by DB vs People object returned from view/page. To update the People object itself, you can use the Update() as given above.

Linq error for nonexistent column

I am in the process of training at an internship to understand asp.net and MVC. We're currently facing a problem that neither I nor my boss can seem to solve.
I have a pre-constructed database of two tables, Camper and Guardian. Camper has a foreign key (guardID) that maps to Guardian's ID.
In my VS project, I have two models, GuardianEntity and CamperEntity. I also used LINQ to SQL to create the database models (dbGuardian and dbCamper). My controller converts the entity model to the db model and vice versa.
Here is the foreign key assignment as LINQ generated it:
[global::System.Data.Linq.Mapping.AssociationAttribute(Name="Guardian_Camper", Storage="_Guardian", ThisKey="guardID", OtherKey="ID", IsForeignKey=true)]
public Guardian Guardian
{
get
{
return this._Guardian.Entity;
}
set
{
Guardian previousValue = this._Guardian.Entity;
if (((previousValue != value)
|| (this._Guardian.HasLoadedOrAssignedValue == false)))
{
this.SendPropertyChanging();
if ((previousValue != null))
{
this._Guardian.Entity = null;
previousValue.Campers.Remove(this);
}
this._Guardian.Entity = value;
if ((value != null))
{
value.Campers.Add(this);
this._guardID = value.ID;
}
else
{
this._guardID = default(int);
}
this.SendPropertyChanged("Guardian");
}
}
}
So it seems to recognize 'guardID' as the foreign key mapping to 'ID'.
But when I run my program and add a camper, it fails on db.saveChanges(). The error says "Invalid column name 'Guardian_ID'."
This question is similar to mine:
entity framework 4.1 invalid column name
Except when I followed one of the solutions offered there (changed my guardID to Guardian_ID as per convention), the error was still thrown, but this time was looking for 'Guardian_ID1'.
We've also noticed that if the column name in the DB for the key is anything other than 'ID', we get an error that says there is no key assigned, even though it is clear in the .dbml that the keys are specified. From what I'm experiencing, Linq won't work well with any column names that aren't what it expects.
So I had this same problem recently and it seems that it was caused by having our migrations out of sync with our Models. What the problem was for me was a parent DataModel referencing the child through an FK. What I had to do was
Remove the references to the "Child" from the "Parent's" Model. Then added a new migration reflecting that change.
Then runUpdate-Database -Script in the Package Manager Console for Visual Studio
When you've got the SQL scripted up, only copy everything after the
INSERT clause. (This is what enters the Migration into the
_MigrationHistory table)
Once you've copied this, paste it into a new query in SSMS. Executing
the query will only add the entry into the history and your
migrations will be "in sync", if you will.
Then for good measure I ran Update-Database again. Then ran my
application again and it worked fine.
So in a nutshell, what ended up causing this problem was that we had removed references from Child to Parent but not the other way around. To LINQ it seems that it thought the relationship still existed so it made that a part of the query.

Is detach necessary when updating record?

I am working with VS2013, .NET4.5, EF6 and SQL 2008.
I have noticed that one of developers in our team changed all the update statements from
if (!journey.WillAnswer.Any())
{
db.WillAnswer.Add(willAnswer);
}
else
{
db.WillAnswer.Attach(willAnswer);
db.Entry(willAnswer).State = EntityState.Modified;
}
db.SaveChanges();
to
if (!journey.WillAnswer.Any())
{
db.WillAnswer.Add(willAnswer);
}
else
{
var will = db.WillAnswer.Single(x => x.Id == willAnswer.Id);
db.Detach(will);
db.WillAnswer.Attach(willAnswer);
db.Entry(willAnswer).State = EntityState.Modified;
}
db.SaveChanges();
Is detach necessary? Is it good practice?
That is certainly bad practice because now there are two will objects floating around in the app that have the same key. One of them is a detached "dummy" and will simply lose any writes made to it. This is error-prone and confusing.
Maybe your app is not using EF like it is supposed to be used. Normally, there is not a lot of attaching and detaching at all. EF objects are meant to be "live" objects representing the database state. They are not DTOs that you create at will and throw away. Especially avoid generic repo methods such as MyRepository.AddOrUpdate taking a temporary entity.
If you want to write to an entity, obtain it and write. Do not create a new entity for updating.

Why Is A Navigation Property Updating Another Entity When Executing An Update In My DataContext? (C# Entity Framework)

I'm running into a situation using the Entity Framework (EF) that has me totally stumped. I'm doing a simple update and the error I'm getting is
Violation of PRIMARY KEY constraint 'PK__tblProducts_Mark__03E07F87'. Cannot insert duplicate key in object 'healthc.tblProducts_MarketSegmentGroups'.
The statement has been terminated.
Let me give you some background on the problem.
I am using Web Forms and have a button click event fire to save some data in several text box controls on my page.
I have a table in my database called tblMetaProducts, which is a table used to store product information from various vendors we work with. The entity for this table is called Products.
I have another table called tblTechAssessment, which holds data for technical questions about the vendor's product, (e.g. what operating system can the software run on, version number etc.). The entity for this table is called TechnicalAssessment. A product can have many technical assessments and they are related by the product id.
I finally have a lookup table in the database called tblProducts_MarketSegmentGroups, which holds a product id and another id (which we don't care about for this problem). The entity for this table is called ProductMarketSegmentGroup. a product can have many product market segment groups and they are releated by the product id.
Here is the code I'm executing to perform the EF save
private void UpdateTechnicalAssessments(int productID)
{
var technicalAssessments = VendorDirectoryController.GetTechnicalAssessments(productID);
var technicalAssessmentTypes = Enum.GetValues(typeof(TechnicalAssessmentType)).Cast<TechnicalAssessmentType>();
foreach (var technicalAssessmentType in technicalAssessmentTypes)
{
var typeName = technicalAssessmentType.ToString();
var id = "SaveToProduction" + typeName + "TextBox";
var results = ProductInformationPanel.FindDescendantsByType<TextBox>().Single(x => x.ID == id).Text;
technicalAssessments.Single(x => x.QuestionID == (int)technicalAssessmentType).Results = results;
}
VendorDirectoryController.SaveChanges();
}
The SaveChanges() method drills down to my domain layer and calls the dataContext.SaveChanges() method.
So my questions are:
1) What can I do to get this to save my TechnicalAssessment entities?
2) Why does my save affect the ProductMarketSegmentGroup entity?
You might be hitting a bug in EF. I also stubmled on something similar (even though I use stored procedures).
The solution was to apply hotfix mentioned in: hotfix: Principal entity in an SQL application generates unnecessary updates - do it still effect EF 4.3.1?
The solution I ended up using was to have our database admin create a proc for the update. I was never able to figure out why the navigation property was causing such a fuss.

Categories