Подтвердить что ты не робот

EF: процесс обновления объекта не меняет значение одного свойства

мое приложение имеет 2 класса: PaymentMethod и Currency (Currency является свойством PaymentMethod). Когда мое приложение обновляет PaymentMethod с новым значением Currency propertyy (значение уже существует в db, но оно назначается на PaymentMethod), после метода SaveCHanges свойство Currency по-прежнему содержит старое значение. ПОЧЕМУ?)

это то, как мое приложение заменяет значение объекта Currency:

 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:

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; }
    }

Способ редактирования:

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 метод:

 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);
        } 

Контекст БД, определенный Autofac:

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

Обновление 1: Журналы EF не показывают обновления поля валюты:

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

Почему нет обновления для свойства Currency?

4b9b3361

Ответ 1

Настройка состояния объекта (кроме Added) влияет только на свойства скалярности объекта, а не на его свойства навигации, его ассоциации.

Итак, у вас есть три варианта:

Вариант 1

Прикрепите валюту к контексту. В вашем методе Edit:

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

Теперь EF знает Currency, который назначен PaymentMethod, поэтому он знает, что ассоциация изменена, и она обновит внешний ключ в базе данных.

Но я не думаю, что это сработает для вас именно так. Из вашего заявления о проблемах я понимаю, что currencyRepo и paymentMethodRepository не разделяют один и тот же контекст, иначе у вас не было бы проблемы в первую очередь (валюта уже будет прикреплена). Вы не можете привязать объект к двум контекстам, поэтому в этой точке должен быть удален контекст currencyRepo, или вы должны сначала отделить валюту от него. Довольно трудоемкий.

Вариант 2

Пусть currencyRepo и paymentMethodRepository (и все хранилища, если на то пошло) имеют один и тот же экземпляр контекста в одной единице работы. Это все равно рекомендуется не только для решения этой проблемы.

Вариант 3

Не задайте свойство Currency, но добавьте свойство примитивного внешнего ключа PaymentMethod.CurrencyId и измените это свойство, если валюта изменится. Это скалярное свойство, поэтому оно будет отвечать настройке EntityState.Modified.

Ответ 2

DbSet.Attach не является рекурсивным. Вам необходимо прикрепить все объекты:

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;
        }

Ответ 3

Эта проблема связана с отношением внешнего ключа, которое не определено в классе PaymentMethod. Это необходимо указать, чтобы конкретный столбец был использован для обновления, и в случае, если валюта является новой, тогда она будет вставлена, и тот же код будет сохранен с кодом валюты.

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 string CurrencyCode {get;set;} //Replace with actual column name

     [ForeignKey("CurrencyCode ")]
    public virtual Currency Currency { get; set; }

}