Context.SubmitChanges() not updating despite having a PK - c#

I am having an issue with the SubmitChanges function provided by the linq to DB implementation in C#. When I run the command, nothing throws an error but the record never gets updated. I have looked up the issue almost everyone says that it is in issue with the table nothing a primary key. However my table has a primary key assigned to it and yet SubmitChanges does not happen. To give you an overview of what I am executing, I here is a sample:
public void setApproval(string approvalCode, int ID)
{
using (DatabaseDataContext context = new DatabaseDataContext(DBConnection().getConnectionString()))
{
myRecord con = getRecord(ID); //Gets the record succesfully, PK field in tact
con.ApprovalStatus = approvalCode;
context.SubmitChanges();
}
}
As commented above, the record is successfully obtained with all the data in tact, including the PK field used to identify it. The database connection user is given the rights to update the table, though here I would expect it to break and complain.
Any ideas? Please let me know if I have not provided enough information. Any help is greatly appreciated!

You should get the object through context
public void setApproval(string approvalCode, int ID)
{
using (DatabaseDataContext context = new DatabaseDataContext(DBConnection().getConnectionString()))
{
myRecord con = context.TableName.First(item => item.ID == ID); //Gets the record succesfully, PK field in tact
con.ApprovalStatus = approvalCode;
context.SubmitChanges();
}
}
When you get the object via Context, it keep track of changes you make and then it save those changes on SubmitChanges

Where does getRecord(ID) get its context to return a record? It is not getting passed to the method, so I assume it is using a different context. SubmitChanges() would only see changes for the current context, not the context that getRecord(ID) used.

Are you checking to see if the data was updated with code or with an independent DB tool?
If in code, your read code is as suspect as the write code:
I was having similar issues when two applications with no common API were communicating through a database. The context is not a reflection of what is in the DB right now, and no amount of telling it to refresh is going to entirely fix the problem. If you need to inspect the database for something entered by another program or thread, you have to create a new database context object to inspect the database. The old database context object may still have the old data from before your most recent update.
Your getRecord function needs to create a new context or take the current context that you just edited as a parameter. If it uses a static or class level context it will not have the latest data.

Related

EntityFramework Core - Copying an entity and placing it back into the database

Is there a best practice for doing a copy of an entity, making some changes to it based on user input, and then re-inserting it into the database?
Some other Stackoverflow threads have mentioned that EF will handle inserting new objects for you even if the same primary key exists in the database, but I'm not quite sure that's how EF Core is handling it. Whenever I try and copy an object I get an error of
Cannot insert explicit value for identity column in table when IDENTITY_INSERT is set to OFF
Basically I just need a clean way to copy an object, make some changes to it based on user input, and then insert that copy back into the database, and have the Id auto-increment properly. Is there a best practice or simple way of doing this without having to manually set properties to null or empty?
EDIT: Example code for retrieving the object from the database:
public Incident GetIncidentByIdForCloning(int id)
{
try
{
return _context.Incident.Single(i => i.IncidentId == id);
}
catch
{
return null;
}
}
Code after retrieving object (As some fields are auto-generated like RowVersion which is a Timestamp):
public IActionResult Clone([FromBody]Incident Incident)
{
var incidentToCopy = _incidentService.IncidentRepository.GetIncidentByIdForCloning(Incident.IncidentId);
incidentToCopy.IncidentTrackingRefId = _incidentService.IncidentRepository.GetNextIdForIncidentCategoryAndType(
Incident.IncidentCategoryLookupTableId, Incident.IncidentTypeLookupTableId).GetValueOrDefault(0);
incidentToCopy.RowVersion = null;
incidentToCopy.IncidentId = 0; //This will fail with or without this line, this was more of a test to see if manually setting would default the insert operation, such as creating a brand new object would normally do.
incidentToCopy.IncidentCategoryLookupTableId = Incident.IncidentCategoryLookupTableId;
incidentToCopy.IncidentTypeLookupTableId = Incident.IncidentTypeLookupTableId;
var newIncident = _incidentService.IncidentRepository.CreateIncident(incidentToCopy);
...
I realize I could just make an entirely new object and do left-hand copying, but that seems terribly inefficient and I want to know if EF Core offers better solutions.
So I went through the "Possible duplicate" thread a bit more than I did when I initially stumbled upon it before creating this one, and there was a not-so-highly upvoted solution that I overlooked that essentially just grabs all of the values at once when retrieving the object from the database - and it doesn't retrieve a reference to that object in the process. My code now looks something like this:
try
{
var incidentToCopy = _context.Incident.Single(i => i.IncidentId == id);
return (Incident) _context.Entry(incidentToCopy).CurrentValues.ToObject();
}
In your IncidentRepository class try getting the Incident by using AsNoTracking and it should get tracked as a new entity when it is added.
public void Clone(int id)
{
// Prevent tracking changes to the object.
var incident = _context.AsNoTracking().SingleOrDefault(i => i.Id == id);
// Setting back to 0 should treat the object Id as unset.
incident.Id = 0;
// Add the Incident while it is untracked will treat it as a new entity.
_context.Incidents.Add(incident);
_context.SaveChanges();
}
I believe what is happening here is the following:
When you retrieve a value from the database, it gets stored in something like
context.ChangeTracker.Entries<Incident>
Which is a collection of Incident entries being tracked. When you change the id property of the incident object you've retrieved you are sort of abusing the ChangeTracker in the name of efficiency. The ChangeTracker does not believe you have created a new object. You might be able to try something like finding the entry in the ChangeTracker and set it's state to detached then after you've set the id to 0 add the object back to the context.DbSet but at that point you have probably made things way more complicated than simply copying the object.

How to deal with a stale cache in Entity Framework?

I had been getting very strange behavior form entity framework. I am coding a WebApi application so the objects I get from the browser are disconnected/detached. The data I get back is transactional such that it does not match any given table in the database. I have to do a number of lookups and data manipulation to get the actual updates to be done on the database.
The problem I seem to have is that in querying the data I am filling up the Tracked Changes cache. That wouldn't seem to be a problem to me since the true source of data should be the database. When I finally make the data changes and I call SaveChanges I get constraint errors. Here are my steps.
Query data.
Create rows to be inserted.
compare rows to db and make db changes.
After reviewing the data in Ctx.ChangeTracker.Entries() I found that an entry to be deleted was marked as Modified when it was supposed to be deleted. The way I worked around it was by Creating a new context for step 3. And it magically started working. I thought that was it, but in my test case I do a last read from the database to verify that my transaction was writing correctly. And I was getting an extra row that should already be deleted. And in fact was, when checking the db directly. Again a new context to do that last read fixed the problem.
I just assumed the default cache setting would just be used to track changes and not to speed up queries.
If I try to use AsNoTracking in my queries I also get into trouble because if I try to delete a row queried like that I get an error. And in my code I don't know if I am going to delete or modify until later on. Is there a way to clear the cache so I don't need to create a new context?
Is there a better way to deal with these issues?
EDIT:
AsNoTracking will do the trick, to some extent. I still found myself instantiating more copies of DbContext in order to prevent errors. Many to one entities have to be deleted in order or null foreign key errors are triggered.
var details = oldInvoice.details.ToList();
Context.Entry(oldInvoice).State = EntityState.Unchanged;
Context.Entry(oldInvoice).State = EntityState.Deleted;
details.ForEach(a => Context.Entry(a).State = EntityState.Deleted);
Entity Framework offers an exception DbUpdateConcurrencyException that you can catch on your calls to SaveChanges(). you could loop through the errors something like this:
catch (DbUpdateConcurrencyException ex)
{
saveFailed = true;
// Get the current entity values and the values in the database
var entry = ex.Entries.Single();
var currentValues = entry.CurrentValues;
var databaseValues = entry.GetDatabaseValues();
// Choose an initial set of resolved values. In this case we
// make the default be the values currently in the database.
var resolvedValues = databaseValues.Clone();
// Have the user choose what the resolved values should be
HaveUserResolveConcurrency(currentValues, databaseValues,
resolvedValues);
// Update the original values with the database values and
// the current values with whatever the user choose.
entry.OriginalValues.SetValues(databaseValues);
entry.CurrentValues.SetValues(resolvedValues);
}
} while (saveFailed);
also, your update code sounds suspicious as well. Usually when you pass data out to a client through WebApi or other mechanisms, the data that is returned doesn't have the tracking data, so you should be checking to see if it exists and re-attaching it to the context and changing it's state to EntityState.Modified if so before calling SaveChanges().

Azure table storage service context continues to throw up the same error if it encounters one error

When i try to update or create an entity on azure table storage, it sometimes throws up an error like "Entity already exists" or "One of the request inputs is out of range", and after that if i try to create or update some other entity on that table also ,it continues to throw up the same error. There is no problem with the input as if I restart the iis server, it starts working again. I have no clue why thats happening.
I tried different SaveChangesOptions including "ContinueOnError" but the table service context continues to throw up error on all further updates/creates after it encounters one error.
Below is my code on how am creating the table storage service context.
Please let me know what the issue could be, this has been a huge blocker for me as major functionalities in my app stop working if the table service context encounters just one error.
public class AudioRecordRepository : Repository<PersistedAudioRecord>, IAudioRecordRepository
{
private TableStorageServiceContext<PersistedAudioRecord> audioRecordServiceContext;
private CloudStorageAccount cloudStorageAccount;
public AudioRecordRepository(IServiceContext<PersistedAudioRecord> serviceContext)
: base(serviceContext)
{
if (RoleEnvironment.IsAvailable)
cloudStorageAccount = CloudStorageAccount.Parse(RoleEnvironment.GetConfigurationSettingValue("StorageConnectionString"));
else
cloudStorageAccount = CloudStorageAccount.Parse(ConfigurationManager.AppSettings["StorageConnectionString"]);
audioRecordServiceContext = new TableStorageServiceContext<PersistedAudioRecord>(TableNames.AudioRecord, cloudStorageAccount.TableEndpoint.ToString(), cloudStorageAccount.Credentials) { IgnoreResourceNotFoundException = true };
}
public bool CreateRecord(PersistedAudioRecord record)
{
this.audioRecordServiceContext.Create(record);
this.audioRecordServiceContext.SaveChangesWithRetries(SaveChangesOptions.ContinueOnError);
return true;
}
}
It appears that the context that contains your entities (audioRecordServiceContext) is declared at the class level and it is not clear if/when it is cleared out.
Table Storage Contexts follow a "Unit of Work" design pattern. They are designed to track entities. If you've added a "bad" entity to your context and tried to persist, you'd obviously get an error. However, what you need to be aware of, is that your context did not throw away the bad entity. It is still tracking it and it still thinks you want to save it. So, next call to SaveChanges will attempt to save it again.
Suggestion: put a "using" statement around your context and declare it inside a function only when you need to save entities - do not let it sit on a class level - unless you have a specific use case that requires it. But if you do, then make sure to yank the failing entity out of the context object (you can do so manually by examining Entities collection of the context object)
HTH
You can recreate the TableServiceContext object for the given table.
TableServiceContext tableServiceContext = tableClient.GetDataServiceContext();
Upgrade to Storage Library 2.0, it's much better, supports far more in terms of querying, and is quite a bit faster to boot.

How to delete an associated object in Entity Framework without having access to the object context

Having two models, Site and Link, where a site has many links, how do I delete a link from inside a method of Site, which doesn't have access to the object context?
I've tried something like:
public void DeleteFirstLink() {
var link = LinkSet.First();
LinkSet.Remove(link);
}
but it seems that is not really deleting the link, but breaking the association. Since there's a database constraints it throws this error:
A relationship is being added or deleted from an AssociationSet 'Sites_have_Links'. With cardinality constraints, a corresponding 'Links' must also be added or deleted.
How do I actually delete the link from the database?
Assuming that your ObjectContext is not alive when you call the DeleteFirstLink() method, you can make it work by spinning up a context inside the method, attaching the Site entity, and then deleting the link:
public void DeleteFirstLink()
{
using (ProjectEntities db = new ProjectEntities())
{
db.AttachTo("[your site EntitySet name - SiteSet?]", this);
var link = LinkSet.First();
db.DeleteObject(link);
LinkSet.Remove(link);
db.SaveChanges();
}
}
You can't delete anything from the database without an object context. All actions are queued in the state manager of the object context, and those are persisted to the database when you call ObjectContext.SaveChanges(). Without SaveChanges, no DB changes.
First of all, it would be great if you could post a bit more information about your class structures. Does the Site class have an ObjectContext object? Then as a quick solution you could pass it into the delete method and use the context.DeleteObject() method, and call SaveChanges afterwards.
However, as a long-term solution, I will still recommend using the UnitOfWork pattern and I will post the link to the article explaining it again. The implementation might be different, but in general it might solve most of your problems (similar to this one).
The beauty of this approach is that if you use it correctly, you can build a small framework, that you can later reuse in all of your EF projects.
To work with entities so the modifications are reflected in the database you MUST ADD/ATTACH these entities in object context (in terms of EF5 in database context) and then use method SaveChanges to commit changes.
Yes, in EF4 to remove a record from phisical SQL table (not a link) you need to use method DeleteObject of object ObjectContext and then SaveChanges, i.e.
using(ObjectContext context = new ObjectContext)
{
/* Find the removed record in object/database context (this will attaches
* the record to object/database context)
* It is recommened to use FirstOrDefault() instead of First()
* becase this method can return null if there is no record to delete
* instead generation of exception in case of using First()
*/
Link linkToDelete = context.Links.FirstOrDefault();
if(linkToDelete != null)
{
context.DeleteObject(linkToDelete);
context.SaveChanges();
}
}
Fortunately now there is EF5 that allows to remove from parent collection but only if relation is one-to-many.
using(DatabaseContext context = new DatabaseContext)
{
Link linkToDelete = context.Links.FirstOrDefault();
if(linkToDelete != null)
{
context.Links.Remove(linkToDelete);
context.SaveChanges();
}
}
In any case DO NOT forget to call SaveChanges!

NHibernate Update Not working

I can't get my update to work. The test fails and I do not see any update statements being sent to the database. Can someone tell me what I'm doing wrong?
This is my repository update procedure:
public void UpdateProject(Project proj)
{
Session.Update(proj);
}
This is the unit test I am trying:
[Test]
public void Can_Update_A_Project()
{
var project = _projects[0];
project.Name = "test project";
repository.UpdateProject(project);
var fromDb = repository.GetAProject(_projects[0].ID);
Assert.AreEqual(project.Name, fromDb.Name);
}
The test always fails. I see the test data being inserted and I see the select for the test.I don't see the update being performed. What am I missing?
Thanks!
There are a couple of things that may be happening.
1) The update is failing and NHibernate is raising an exception that is being swallowed somewhere - that can happen depending on how you've configured things. So in VS make sure all exceptions will cause a break.
2) The update is being cached and not written directly to the DB - you can force data to be written using Repository.Flush();
3) Are you sure _projects[0] has been read from the DB - I'm assuming that's happening in a TestSetup? If not NHibernate won't be aware of that as an object which is under its 'control'.
BTW - It's good practise to read the data you are going to change within the test itself, and then undo that change, resetting the DB to it's original state. In that way your test DB won't be altered by your testing.
BTW2 - In the above test, if project.Name has already been updated once, ie the test has run succesfully. Then next time around the test will suceed even if the update itself fails. A way to avoid this - append a DateTime to the project.Name, don't set it to a fixed value.
Another thing is this: when you save an entity using a session and you load the same entity from the same session using the entity's ID, you will get the same instance that you saved - regardless of whether any inserts or updates have been issued to the database.
That's because of NHibernate's 1st level cache, which is an identity map that belongs to the session.
If you want your test to check what was actually written to the database, you may do it like so:
session.Save(someEntity);
session.Flush(); // forces the entity to be inserted
session.Clear(); // clears the session's identity map, thus
// detaching someEntity from the session
var loadedEntity = session.Get<EntityType>(someEntity.Id);
// now you may compare the fields of someEntity and loadedEntity
// to verify that they were actually persisted
ISession.Update in NHibernate does not commit changes to the database. It is used to update transient instances in a different session from the one that was used to retrieve the instance (see here for details). Changes are sent to the database when a session is flushed. By default, sessions operate in FlushOnCommit mode, which means the changes to the objects will be sent to the database when the NHibernate transaction is committed (see here for details on the different flush modes).
try this may be i am wrong but its works
public void UpdateProject(Project proj)
{
ISessionFactory SessionFactory;
ISession session = SessionFactory.OpenSession();
using (ITransaction transaction = session.BeginTransaction())
{
session.Update(proj);
transaction.Commit();
}
}
FlushMode!
I'm maintaining an app I did not write and found out the hard way that even if you use a transaction and call txn.Commit() you still may not see the changes if your NHibernate Session has session.FlushMode = FlushMode.Never.
Very easy to detect. Set a breakpoint and look at session.FlushMode. Or just search all *.cs files for FlushMode.
I recently hit this same issue.
Inserts Worked
Updates Did Not
The issue was a flaw in the coding. Data was checked if the row was changed, then a new entity was mapped, and an update was sent, but nothing happened.
Solution: The actual row had to be queried from the database, then changes in C# applied to that row that was pulled back. Now NHibernate knows the row changed, run save and all worked.
public void SaveChanges()
{
_session.Flush();
_session.Transaction.Commit();
_session.BeginTransaction();
}

Categories

Resources