Experiencing an issue about updating mysql DB through EF. It's not the first time I'm dealing with it, so I had some ideas about why isn't my data getting changed. I tried changing an element in goods array; tried editing an object, recieved through LINQ-request (seen some examples of this method); made some attempts on marking element found in the database before editing (like EntityState and Attach()). Nothing of these made any difference, so I tried removing <asp:UpdatePanel> from Site.Master to see what happens (responsive for postback blocking to prevent page shaking on update), but nothing changed (while btnRedeemEdit.IsPostBack having its default value).
Code below is the function I use for updates.
protected void btnRedeemEdit_Click(object sender, EventArgs e)
{
if (!string.IsNullOrEmpty(Request.QueryString["id"]))
{
var db = new GoodContext();
var goods = db.Goods.ToList();
Good theGood = goods.FirstOrDefault(x => x.Id == int.Parse(Request.QueryString["id"]));
//db.Goods.Attach(theGood);//No effect
//db.Entry(theGood).State = System.Data.Entity.EntityState.Modified; //No effect
if (theGood != default)
{
theGood.AmountSold = GetInput().AmountSold;
theGood.APF = GetInput().APF;
theGood.Barcode = GetInput().Barcode;
theGood.Description = GetInput().Description;
theGood.ImagesUrl = GetInput().ImagesUrl;//"https://i.pinimg.com/564x/2d/b7/d8/2db7d8c53b818ce838ad8bf6a4768c71.jpg";
theGood.Name = GetInput().Name;
theGood.OrderPrice = GetInput().OrderPrice;
theGood.Profit = GetInput().Profit;
theGood.RecievedOn = GetInput().RecievedOn;//DateTime.Parse(GetInput().RecievedOn).Date.ToString();
theGood.TotalAmount = GetInput().TotalAmount;
theGood.WeightKg = GetInput().WeightKg;
//SetGoodValues(goods[editIndex],GetInput());//Non-working
db.SaveChanges();
Response.Redirect("/AdminGoods");
}
else Response.Write($"<script>alert('Good on ID does not exist');</script>");
}
else Response.Write($"<script>alert('Unable to change: element selected does not exist');</script>");
}
Notice, that no alerts appear during execution, so object in database can be found.
Are there any more things, that can be responsible for blocking database updates?
A few things to update & check:
Firstly, DbContexts should always be disposed, so in your case wrap the DbContext inside a using statement:
using (var db = new GoodContext())
{
// ...
}
Next, there is no need to load all goods from the DbContext, just use Linq to retrieve the one you want to update:
using (var db = new GoodContext())
{
Good theGood = db.Goods.SingleOrDefault(x => x.Id == int.Parse(Request.QueryString["id"]));
if (theGood is null)
{
Response.Write($"<script>alert('Good on ID does not exist');</script>");
return;
}
}
The plausible suspect is what does "GetInput()" actually do, and have you confirmed that it actually has the changes you want? If GetInput is a method that returns an object containing your changes then it only needs to be called once rather than each time you set a property:
(Inside the using() {} scope...)
var input = GetInput();
theGood.AmountSold = input.AmountSold;
theGood.APF = input.APF;
theGood.Barcode = input.Barcode;
theGood.Description = input.Description;
// ...
db.SaveChanges();
If input has updated values but after calling SaveChanges you aren't seeing updated values in the database then there are two things to check.
1) Check that the database connection string at runtime matches the database that you are checking against. The easiest way to do that is to get the connection string from the DbContext instance's Database.
EF 6:
using (var db = new GoodContext())
{
var connectionString = db.Database.Connection.ConnectionString; // Breakpoint here and inspect.
EF Core: (5/6)
using (var db = new GoodContext())
{
var connectionString = db.Database.GetConnectionString();
Often at runtime the DbContext will be initialized with a connection string from a web.config / .exe.config file that you don't expect so you're checking one database expecting changes while the application is using a different database / server. (More common than you'd expect:)
2) Check that you aren't disabling tracking proxies. By default EF will enable change tracking which is how it knows if/when data has changed for SaveChanges to generate SQL statements. Sometimes developers will encounter performance issues and start looking for ways to speed up EF including disabling change tracking on the DbContext. (A fine option for read-only systems, but a pain for read-write)
EF6 & EF Core: (DbContext initialization)
Configuration.AutoDetectChangesEnabled = false; // If you have this set to false consider removing it.
If you must disable change tracking then you have to explicitly set the EntityState of the entity to Modified before calling SaveChanges():
db.Entry(theGood).State = EntityState.Modified;
db.SaveChanges();
Using change tracking is preferable to using EntityState because with change tracking EF will only generate an UPDATE statement if any values have changed, and only for the values that changed. With EntityState.Modified EF will always generate an UPDATE statement for all non-key fields regardless if any of them had actually changed or not.
Related
I have an external API resource that I need to use. The API sends me trigger as a post request. What I need to do is get the values, and update the current corresponded record in the SQL. The code that I came up with keeps adding a new value to the database.
[HttpPost("status")]
public async Task<IActionResult> Post(CompletionForCreateDto
completionForCreateDto)
{
// che ck the model state
if (!ModelState.IsValid)
return BadRequest(ModelState);
// find the incoming trigger information and map them to the disctionary
var form = Request.ReadFormAsync();
var formData = form.Result;
Dictionary<string, string> ddata = new Dictionary<string, string>();
foreach ( var key in formData.Keys)
{
var value = formData[key.ToString()];
ddata.Add(key, value);
}
// find the existed completion by ID
int id = Int32.Parse(ddata["record"]);
var completion = await repository.GetCompletion(id);
completion = mapper.Map<CompletionForCreateDto, Completion>(completionForCreateDto);
if (completion == null)
return NotFound();
// some test values to make sure it works.
completion.VitalSignBLA = ddata["vital_signs_spring_complete"];
completion.VitalSignBLADateTime = DateTime.Now;
// repository.Add(completion); ==> adds value as a new record
// repository.Update(completion);==> adds value as a new record
repository.Attach(completion); // ==> adds value as a new record
// SaveChanges()
await unitOfWork.CompleteAsync();
return Ok();
}
Above, first I get the trigger and put them into a dicmtionary so that I can use. Then, I tried to find the existed trigger record in my sql servere using their record value. Trigger's record and SQL record value are the same values. After that, I use some data to test it. Lastly, I used context Add, Update and Attach to update the SQL database. For all cases, the post request is kept adding a new row.
I am new to the dotnet core. I am not sure it this because I used HttpPost attribute or the code itself. Any help wpuld be appriciated!
The most probable cause for EF generating an insert statement for an entity when attaching it to a context (Through Attach, Update, Add or by setting its State) is missing values from the PK (most commonly the ID, but the PK can be more complex in other cases) for that entity.
When no PK is present, EF cannot know which entity you are referring to, so it sets the state to New. In your code, I suspect this is generating your issues:
var completion = await repository.GetCompletion(id);
completion = mapper.Map<CompletionForCreateDto, Completion>(completionForCreateDto);
You are getting some data from the repository, but then generating a new entity through mapper.Map(completionForCreateDto);. I suspect that this code is not setting the PK values, causing your issues. A naive fix would be to set these values after mapper.map. That, or update the mappings to map the values.
I am using EF5 and Data First approach to Update entities.
I am using approach suggested by other questions to conditionally update only modified properties in the Entities.
Oki so here's the scenario My controller call Service with POCO objects and gets POCO objects from Service, The Service layer talks with Data layer which internally uses EF5 to retrieve entity from DB and Update them in DB.
The View data is loaded by controller from DTO object retrieved from Service layer.
User makes changes to View and Posts back JSON data to controller which gets mapped to DTO object in controller (courtesy MVC).
The controller makes call to Service layer with the DTO object (POCO) object.
The Service maps the POCO object to EF entity object and calls the Data layer's(i.e Repository) Update method passing in the EF entity.
In the Repository I fetch the existing entity from DB and call ApplyCurrentvaluesValues method, then I check if any properties are modified .
If properties are modified then I apply my custom logic to other entities which are not related to current entity and also Update the "UpdatedAdminId" & "UpdationDate" of current entity.
Post this I call "SaveChanges" method on Centext.
Every thing above I mentioned is working fine , except if I insert a break point in "SaveChanges" call and update some field modified by User to different value then "DbUpdateConcurrencyException" is not thrown by EF5.
i.e. I can get conditional Update & fire my custom logic when properties of my interest are modified to work perfectly.
But I am not getting error in case of the concurrency i.e the EF is not raising "DbUpdateConcurrencyException" in case a record is updated in between me fetching the record from DB , updating the record and saving it.
In real scenario there is a offline cron running which checks for newly created campaign and creates portfolio for them and marks the IsPortfolioCreated property below as true, in the mean time user can edit the campaign and the flag can be set to false even though the cron has created the portfolios.
To replicate the concurrency scenario I put a break point on SaveChanges and then Update the IsPortfolioCreated feild from MS-Sql enterprise manager for the same entity, but the "DbUpdateConcurrencyException" is not thrown even though the Data in Store has been updated.
Here's my code for reference,
Public bool EditGeneralSettings(CampaignDefinition campaignDefinition)
{
var success = false;
//campaignDefinition.UpdatedAdminId is updated in controller by retreiving it from RquestContext, so no its not comgin from client
var updatedAdminId = campaignDefinition.UpdatedAdminId;
var updationDate = DateTime.UtcNow;
CmsContext context = null;
GlobalMasterContext globalMasterContext = null;
try
{
context = new CmsContext(SaveTimeout);
var contextCampaign = context.CampaignDefinitions.Where(x => x.CampaignId == campaignDefinition.CampaignId).First();
//Always use this fields from Server, no matter what comes from client
campaignDefinition.CreationDate = contextCampaign.CreationDate;
campaignDefinition.UpdatedAdminId = contextCampaign.UpdatedAdminId;
campaignDefinition.UpdationDate = contextCampaign.UpdationDate;
campaignDefinition.AdminId = contextCampaign.AdminId;
campaignDefinition.AutoDecision = contextCampaign.AutoDecision;
campaignDefinition.CampaignCode = contextCampaign.CampaignCode;
campaignDefinition.IsPortfolioCreated = contextCampaign.IsPortfolioCreated;
var campaignNameChanged = contextCampaign.CampaignName != campaignDefinition.CampaignName;
// Will be used in the below if condition....
var originalSkeForwardingDomain = contextCampaign.skeForwardingDomain.ToLower();
var originalMgForwardingDomain = contextCampaign.mgForwardingDomain.ToLower();
//This also not firing concurreny exception....
var key = ((IObjectContextAdapter) context).ObjectContext.CreateEntityKey("CampaignDefinitions", campaignDefinition);
((IObjectContextAdapter)context).ObjectContext.AttachTo("CampaignDefinitions", contextCampaign);
var updated = ((IObjectContextAdapter)context).ObjectContext.ApplyCurrentValues(key.EntitySetName, campaignDefinition);
ObjectStateEntry entry = ((IObjectContextAdapter)context).ObjectContext.ObjectStateManager.GetObjectStateEntry(updated);
var modifiedProperties = entry.GetModifiedProperties();
//Even tried this , works fine but no Concurrency exception
//var entry = context.Entry(contextCampaign);
//entry.CurrentValues.SetValues(campaignDefinition);
//var modifiedProperties = entry.CurrentValues.PropertyNames.Where(propertyName => entry.Property(propertyName).IsModified).ToList();
// If any fields modified then only set Updation fields
if (modifiedProperties.Count() > 0)
{
campaignDefinition.UpdatedAdminId = updatedAdminId;
campaignDefinition.UpdationDate = updationDate;
//entry.CurrentValues.SetValues(campaignDefinition);
updated = ((IObjectContextAdapter)context).ObjectContext.ApplyCurrentValues(key.EntitySetName, campaignDefinition);
//Also perform some custom logic in other entities... Then call save changes
context.SaveChanges();
//If campaign name changed call a SP in different DB..
if (campaignNameChanged)
{
globalMasterContext = new GlobalMasterContext(SaveTimeout);
globalMasterContext.Rename_CMS_Campaign(campaignDefinition.CampaignId, updatedAdminId);
globalMasterContext.SaveChanges();
}
}
success = true;
}
catch (DbUpdateConcurrencyException ex)
{
//Code never enters here, if it does then I am planning to show the user the values from DB and ask him to retry
//In short Store Wins Strategy
//Code in this block is not complete so dont Stackies don't start commenting about this section and plague the question...
// 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();
// 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);
}
catch (Exception ex)
{
if (ex.InnerException != null)
throw ex.InnerException;
throw;
}
finally
{
if (context != null) context.Dispose();
if (globalMasterContext != null) globalMasterContext.Dispose();
}
return success;
}
Entity framework it's not doing anything special about concurrency until you (as developer) configure it to check for concurrency problems.
You are trying to catch DbUpdateConcurrencyException, the documentation for this exception says: "Exception thrown by DbContext when it was expected that SaveChanges for an entity would result in a database update but in fact no rows in the database were affected. ", you can read it here
In a database first approach, you have to set the property 'Concurrency Mode' for column on 'Fixed' (the default is None). Look at this screenshot:
The column Version is a SQL SERVER TIMESTAMP type, a special type that is automatically updated every time the row changes, read about it here.
With this configuration, you can try with this simple test if all is working as expected:
try
{
using (var outerContext = new testEntities())
{
var outerCust1 = outerContext.Customer.FirstOrDefault(x => x.Id == 1);
outerCust1.Description += "modified by outer context";
using (var innerContext = new testEntities())
{
var innerCust1 = innerContext.Customer.FirstOrDefault(x => x.Id == 1);
innerCust1.Description += "modified by inner context";
innerContext.SaveChanges();
}
outerContext.SaveChanges();
}
}
catch (DbUpdateConcurrencyException ext)
{
Console.WriteLine(ext.Message);
}
In the example above the update from the inner context will be committed, the update from the outer context will thrown a DbUpdateConcurrencyException, because EF will try to update the entity using 2 columns as a filters: the Id AND the Version column.
Hope this helps!
This is EF 7 (now Core). I have a shadow property named CreatedBy. EF correctly loads its value from data store, the value of this property remains accessible as long as I use the same DbContext instance but I need to work in detached way, and submit changes later using another DbContext instance.
The problem is that for subsequent DbContexts all shadows properties are NULL, and so far I cannot see anyway to get it loaded. So before get rid of this shadows I need to know if somebody already have come across for a solution for this issue.
If it could help, here is how proceeding with here:
var cached = Cache.Get<MyType>();
cached.Default = false; //some updating
var dbContext = new MyContext();
dbContext.Attach( cached );
dbContext.Entry( cached ).State = EntityState.Modified;
dbContext.SaveChanges();
SaveChanges is submitting NULL value for shadows property.
You need to attach the cached object directly to the table/object it belongs to instead of directly to the context. For example:
var existingBlog = new Blog { BlogId = 1, Name = "ADO.NET Blog" };
using (var context = new BloggingContext())
{
context.Blogs.Attach(existingBlog);
context.Entry(existingBlog).State = EntityState.Modified;
context.SaveChanges();
}
You are doing context.Attach() directly and this will not work. Go here, https://msdn.microsoft.com/en-us/data/jj592676.aspx, for more info.
var paymentAttempt = _auctionContext.PaymentAttempts.Where(o => o.Id == paymentAttemptId).SingleOrDefault();
if (paymentAttempt != null)
{
paymentAttempt.PaymentAttemptStatusId = (int)PaymentAttemptStatus.Defunct;
paymentAttempt.PaymentAttemptStatus = _auctionContext.PaymentAttemptStatuses.Where(pas => pas.Id == paymentAttempt.PaymentAttemptStatusId).First();
var relevantWinningBidsTotalPrices = _auctionContext.GetWinningBidsTotalPricesForPaymentAttempt(paymentAttemptId).ToArray();
foreach (var winningBid in relevantWinningBidsTotalPrices)
{
winningBid.Locked = false;
_auctionContext.UpdateObject(winningBid);
}
_auctionContext.SaveChanges();
}
In the above code after
_auctionContext.SaveChanges();
is called winningBid is updated as expected but paymentAttempt isn't. Why is this? It is really frustrating. There is no error either. I would expect a failure to occur if there was a problem like EF wasn't tracking the object or something like that, but no such error is happening.
That's because you need to pass the paymentAttempt object to your context, to let it know that it is an object that needs to be updated.
For example, assuming that _auctionContext is an instance of DbContext:
// any changes related to the paymentAttempt object
_auctionContext.Entry(paymentAttempt).State = EntityState.Modified;
foreach (var winningBid in relevantWinningBidsTotalPrices)
{
winningBid.Locked = false;
_auctionContext.UpdateObject(winningBid);
}
_auctionContext.SaveChanges();
Another option is the Attach method:
_auctionContext.Attach(paymentAttempt);
_auctionContext.ObjectStateManager.ChangeObjectState(paymentAttempt, System.Data.EntityState.Modified);
If you don't have Entry try adding:
using System.Data.Entity.Infrastructure;
using System.Data.Entity;
then you may simply use:
_auctionContext.Entry(paymentAttempt).State = EntityState.Modified;
_auctionContext.SaveChanges();
I fell on this question but for a different problem. I discovered that if you call SaveChanges() on an object that hasn't been modified, EF will not update anything. This makes sense, but I needed the DB to be updated so that other users would see that a SaveChanges() had been executed, regardless of whether any fields had changed. To force an update without changing any fields:
Dim entry As DbEntityEntry = entities.Entry(myentity)
entry.State = Entity.EntityState.Modified
I know this is late but there's another explanation worth mentioning. Even though your field name contains ID and may be set to autoincrement, be sure to verify that you declared it in your table the primary key.
The following code gets all the rows from my Activities table that have not already been posted on Twitter. It then loops through and posts Twitter updates for each of those row. In the process, I would like to update the database to indicate these rows have now been "twittered".
However, I'm getting an error (indicated below) when I try and update this value. I assume this is because I'm using an anonymous type. However, if I use the full type, that will require pulling a lot of unnecessary data from the database.
Is there a way to accomplish this efficiently? Or is this yet another case where EF forces me to make compromises in performance?
using (MyEntities context = new MyEntities())
{
var activities = from act in context.Activities
where act.ActTwittered == false
select new { act.ActID, act.ActTitle, act.Category,
act.ActDateTime, act.Location, act.ActTwittered };
foreach (var activity in activities)
{
twitter.PostUpdate("...");
activity.ActTwittered = true; // <== Error: ActTwittered is read-only
}
}
You could try a "fake object approach" like this:
using (MyEntities context = new MyEntities())
{
var activities = from act in context.Activities
where act.ActTwittered == false
select new { act.ActID, act.ActTitle, act.Category,
act.ActDateTime, act.Location, act.ActTwittered };
foreach (var activity in activities)
{
twitter.PostUpdate("...");
// Create fake object with necessary primary key
var act = new Activity()
{
ActID = activity.ActID,
ActTwittered = false
};
// Attach to context -> act is in state "Unchanged"
// but change-tracked now
context.Activities.Attach(act);
// Change a property -> act is in state "Modified" now
act.ActTwittered = true;
}
// all act are sent to server with sql-update statements
// only for the ActTwittered column
context.SaveChanges();
}
It's "theoretical" code, not sure if it would work.
Edit
Not "theoretical" anymore. I've tested this with DbContext of EF 4.1 and it works as described in the sample code above. (Because DbContext is only a wrapper API around ObjectContext it's almost safe to assume that it also will work in EF 4.0.)
If you simply select 'act', then it should work. Don't forget to submit after editing.
Why are you calling select new instead of returning entire object. Entity framework will only be able to update property if it is correctly defined in schema resources which certainly is not case with anonymous type.
Entity framework will never be able to determine which table and which field the property is mapped to.