8

I have a Payment model with a 'Status' boolean value which defaults to false. Once payment has been made, I need to update that specific payment's 'Status' to true.

Here's the code I've been trying to use to change the specific database entry, but it's just not changing it. What am I doing wrong?

Payment payment = new Payment();
payment = db.Payments.Find(orderId);
db.Entry(payment).State = EntityState.Modified;
payment.Status = true;
db.SaveChanges();

Thanks!

This is what ended up working:

using (var con = new ApplicationDbContext())
{
    payment = con.Payments.First(x => x.Id == orderId);
    payment.Status = true;

    con.Payments.Attach(payment);
    var entry = con.Entry(payment);
    entry.Property(e => e.Status).IsModified = true;
    con.SaveChanges();
}
Pierre
  • 330
  • 1
  • 3
  • 20
  • Is db.Payments.Find(orderId) returns you a payment? – Alex Art. Sep 09 '14 at 19:30
  • Yes, the orderId is to identify which payment should be modified (the orderId is the same as the Payment ID that was created when the payment entry was made). – Pierre Sep 09 '14 at 19:33
  • What version of entity framework are you using? – Alex Art. Sep 09 '14 at 19:34
  • Entity Framework: v4.0.30319 – Pierre Sep 09 '14 at 19:36
  • 2
    I see no reason why what you are doing here would not work. Is this really all you are doing or are you doing more than this, but have tried to simplify in order to ask your question? Please add the code for your `Payment` class. – Jeremy Cook Sep 09 '14 at 19:55
  • Do other properties update? – mxmissile Sep 09 '14 at 20:03
  • You might have a look at my answer on [ASP.NET MVC - Attaching an entity of type 'MODELNAME' failed because another entity of the same type already has the same primary key value](http://stackoverflow.com/questions/23201907/asp-net-mvc-attaching-an-entity-of-type-modelname-failed-because-another-ent/39557606#39557606). – Murat Yıldız Sep 18 '16 at 12:30

5 Answers5

10
Payment payment = new Payment();
payment = db.Payments.Find(orderId);
payment.Status = true;
db.Entry(payment).State = EntityState.Modified;
db.SaveChanges();
ali golshani
  • 124
  • 1
  • 4
3

The reason all of these are failing is because either the Payment object is never attached to the DBContext or the orderId doesn't actually match up with the PK on the Payments table. In order for SaveChanges() to actually work, the object you're changing needs to be tracked by the DBContext, not just have its EntityState set to Modified. Also all these examples seem grossly overcomplicated.

using (var db = new DbContext())
{
    // make sure you have the right column/variable used here
    var payment = db.Payments.FirstOrDefault(x => x.Id == orderId);

    if(payment == null) throw new Exception("Invalid id: " + orderId);

    // this variable is tracked by the db context
    payment.Status = true;

    db.SaveChanges();
}
Radu Porumb
  • 785
  • 5
  • 7
  • I've verified that the orderId is indeed the correct value for the payment Id that I want to change. I've tried your code, but it still doesn't update that entry. If I replace this code with an "add new entry to Payment" then the code works fine, so the code does execute, but no changes are made with the updating of the entry. – Pierre Sep 10 '14 at 13:59
  • @Pierre Which version of Entity Framework are you using? And is this code running inside a transaction at any layer? – Radu Porumb Sep 10 '14 at 15:06
  • Entity Framework: v4.0.30319. I don't use transactions at all. – Pierre Sep 10 '14 at 16:05
  • @Pierre Did you set any custom options on your DbContext? – Radu Porumb Sep 10 '14 at 16:54
  • Nope, only using public class ApplicationDbContext : IdentityDbContext { public ApplicationDbContext() : base("DefaultConnection") { } } Which is the default that the MVC project provides – Pierre Sep 10 '14 at 18:18
  • I can also edit payments using the strongly typed views made directly from the model. So the entries do update with that, just not with my own code. – Pierre Sep 10 '14 at 18:26
2

If you just have your entity id, just do like Ali Golshani said. But if you already have the entity and just want to update, you can do this way:

 public void Update(Payment payment, int orderId)
    {
        //first find the entity to update
        Payment oldEntity = DbSet.Find(orderId);

        //detach this entity from the DbSet
        Db.Entry(oldEntity).State = EntityState.Detached;

        //set the state from the entity that you just received to modified 
        Db.Entry(obj).State = EntityState.Modified;
    }

Detaching avoids the error message: "Attaching an entity failed because another entity of the same type already has the same primary key value".

I hope that it helps.

Pedro S Cord
  • 1,301
  • 17
  • 20
  • This pattern is very useful if you are returning a record from an HTML post into an MVC controller and want to update the values in an existing record. Make sure that you substitute the 'obj' for the record (object) that you are passing in. (in this case it would be payment) – pat capozzi Mar 24 '22 at 02:53
1

Try this one:

    Payment payment;
    using (var context = new DBContext()) //replace the name of your context
    {
        payment = context.Payments.Find(orderId);
    }

    if(payment != null)
    {
       payment.Status = true;
    }

    using (var context = new DBContext()) //replace the name of your context
    {
        context.Payments.Attach(payment);
        context.Entry(payment).State = System.Data.EntityState.Modified;    
        context.SaveChanges();
    }
Alex Art.
  • 8,711
  • 3
  • 29
  • 47
  • 3
    Why do you create (and dispose of) two separate `DbContexts` for this simple update? – MEMark Sep 09 '14 at 19:48
  • The whole update can definitely be performed under the single context. I wrote it this way because i don't know whether he works with attached entities or with detached. Attached entities demand using the same context both for select an update – Alex Art. Sep 09 '14 at 19:55
  • Still not working. Maybe the problem lies with the orderId. I'll check if the correct orderId is posted from the payment website. But the only difference between our code is the line of code with "Attach". I've never used this term, will also read up on that. Thanks for the answer, will mark it as answered as soon as I've sorted this problem out :) – Pierre Sep 09 '14 at 20:01
  • I've verified that orderId is correct and I still can't get the entry to update. – Pierre Sep 10 '14 at 14:00
1

As mentioned here:

The Find method on DbSet uses the primary key value to attempt to find an entity tracked by the context. If the entity is not found in the context then a query will be sent to the database to find the entity there. Null is returned if the entity is not found in the context or in the database.

So be sure that Payment class looks like this:

public class Payment
{
     [Key]
     public int Id {get; set;}
     public bool Status {get; set;}
}

And your Entry save logic could look like this:

Payment payment = null;
using (var ts = new TransactionScope(TransactionScopeOption.Required, new TimeSpan(1, 0, 0, 0)))
{
    using (var context = new DBContext()) 
    {
        context.Database.Log = s => { System.Diagnostics.Debug.WriteLine(s); };
        payment = context.Payments.Find(orderId);
        if(payment != null)
        {
            payment.Status = true;
            context.Entry(payment).State = System.Data.EntityState.Modified;    
        }
        else 
        {
            context.Payments.Add(new Payment(){
                Status = true
            });
        }
        context.SaveChanges();
    }
    ts.Complete();
}

Added transaction scope to be sure that it is properly open and close, and added sql query logging to debug window.

aleha_84
  • 8,309
  • 2
  • 38
  • 46
  • It's still not changing the State of the payment. It reaches the "if" statement and not the "else", but it doesn't change the state. The OrderId is correct, but it doesn't update that Payment for some reason... – Pierre Sep 09 '14 at 20:24
  • Maybe you run your code in transaction and not commiting it? Try to wrap db context in TransactionScope as in example below. And check SQL-query which generated by EF by using its logging mechanizm. – aleha_84 Sep 10 '14 at 07:00