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?
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:
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.
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.
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
.
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;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With