Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

EF: object update process is not changing value of one property

my application has 2 classes: PaymentMethod & Currency (Currency is property of PaymentMethod). When my app does update of PaymentMethod with new value of Currency propery (value already exists in db but it's being assigned to PaymentMethod), after SaveCHanges method, Currency property still contains old value. WHY?:)

this is how my application replaces Currency object value:

 if (String.Compare(existingPaymentMethod.Currency.Code, mwbepaymentmethod.CurrencyCode, true) !=0)
            {
                var readCurrency = currencyRepo.FindByCode(mwbepaymentmethod.CurrencyCode);

                existingPaymentMethod.Currency = readCurrency;

            }

            paymentMethodRepository.Save(ref existingPaymentMethod);
            return true;

PaymentMethod & Currency classes:

public class PaymentMethod : BaseEntity
    {
        public enum MethodTypeEnum
        {
            Creditcard,
            Virtualcard,
            Wallet
        };
        public MethodTypeEnum MethodType { get; set; }
        public int VendorId { get; set; }
        public virtual Address BillingAddress { get; set; }
        public virtual Currency Currency { get; set; }
    }

public class Currency : BaseEntity
    {
        [JsonProperty("code")]
        [Key]
        public string Code { get; set; }

        [JsonProperty("symbol")]
        public string Symbol { get; set; }

        [JsonIgnore]
        public virtual ICollection<Payment> Payments { get; set; }

        [JsonIgnore]
        public virtual ICollection<PaymentMethod> PaymentMethods { get; set; }
    }

Edit method:

public override void Edit(MwbePaymentMethod entityToUpdate)
        {
            DbSet.Attach(entityToUpdate);
            Context.Entry(entityToUpdate).State = EntityState.Modified;

            //manual update of  properties
            //Context.Entry(entityToUpdate.BillingAddress).State = EntityState.Modified;
            //Context.Entry(entityToUpdate.Currency).State = EntityState.Unchanged;
        }

OnModelCreating method:

 protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            Database.SetInitializer(new DropCreateDatabaseIfModelChanges<MobileWalletContext>());
           ...
            modelBuilder.Entity<MwbePaymentMethod>().HasRequired(e => e.Currency).WithMany(e => e.PaymentMethods);

            base.OnModelCreating(modelBuilder);
        } 

DB context defined by Autofac:

builder.RegisterType<MobileWalletContext>().As<IMwbeDbContext>().InstancePerRequest();

Update 1: EF logs shows no currency field update:

UPDATE [dbo].[MwbeAddress] SET [Street] = @0, [City] = @1, [ZipCode] = @2, [Country] = @3 WHERE ([Id] = @4)
-- @0: 'FFFF12' (Type = String, Size = -1)
-- @1: 'GlasgowSSSS' (Type = String, Size = -1)
-- @2: 'B33 8TH' (Type = String, Size = -1)
-- @3: 'England' (Type = String, Size = -1)
-- @4: '2' (Type = Int32)
-- Executing at 2015-07-13 07:35:48 +02:00
-- Completed in 39 ms with result: 1

UPDATE [dbo].[MwbePaymentMethod] SET [MethodType] = @0, [VendorId] = @1, [Number] = @2, [ExpirationDate] = @3, [Balance] = @4, [IsPending]
= @5, [IsDefault] = @6 WHERE ([Id] = @7)
-- @0: '1' (Type = Int32)
-- @1: '0' (Type = Int32)
-- @2: '4444 4444 4444 4450' (Type = String, Size = -1)
-- @3: '2015-10-10 00:00:00' (Type = DateTime2)
-- @4: '0' (Type = Double)
-- @5: 'True' (Type = Boolean)
-- @6: 'False' (Type = Boolean)
-- @7: '3' (Type = Int32)
-- Executing at 2015-07-13 07:35:48 +02:00
-- Completed in 7 ms with result: 1

Why there is not update for Currency property?

like image 557
P.K. Avatar asked Jul 12 '15 23:07

P.K.


2 Answers

Setting an entity's state (other than Added) only affects the entity's scalar properties, not its navigation properties, its associations.

So you have three options:

Option 1

Attach the currency to the context. In your Edit method:

Context.Entry(entityToUpdate).State = EntityState.Modified;
Context.Entry(entityToUpdate.Currency).State = EntityState.Unchanged;

Now EF knows the Currency that is assigned to PaymentMethod, so it knows the association changed, and it will update the foreign key in the database.

But I don't think this is going to work for you just like that. From your problem statement I understand that currencyRepo and paymentMethodRepository don't share the same context, otherwise you wouldn't have had the problem in the first place (the currency would already be attached). You can't attach an entity to two contexts, so either currencyRepo's context should be disposed at that point, or you should detach the currency from it first. Pretty laborious.

Option 2

Let currencyRepo and paymentMethodRepository (and all repositories for that matter) share the same context instance within one unit of work. This is recommended anyway, not only to solve this problem.

Option 3

Don't set the Currency property, but add a primitive foreign key property PaymentMethod.CurrencyId and modify that property if the currency changes. This is a scalar property, so it will respond to setting EntityState.Modified.

like image 151
Gert Arnold Avatar answered Nov 19 '22 07:11

Gert Arnold


DbSet.Attach is not recursive. You need to attach all the entities involed:

public override void Edit(MwbePaymentMethod entityToUpdate)
        {
            DbSet.Attach(entityToUpdate);
            Context.Entry(entityToUpdate).State = EntityState.Modified;

            if(entityToUpdate.BillingAddress != null)
            {
              DbSet.Attach(entityToUpdate.BillingAddress);
              Context.Entry(entityToUpdate.BillingAddress).State = EntityState.Modified;
            }

            if(entityToUpdate.Currency != null)
            {
              DbSet.Attach(entityToUpdate.Currency);
              Context.Entry(entityToUpdate.Currency).State = EntityState.Modified;
            }

            //manual update of  properties
            //Context.Entry(entityToUpdate.BillingAddress).State = EntityState.Modified;
            //Context.Entry(entityToUpdate.Currency).State = EntityState.Unchanged;
        }
like image 5
pquest Avatar answered Nov 19 '22 06:11

pquest