Невозможно обновить значение внешнего ключа с помощью Entity Framework - PullRequest
0 голосов
/ 17 января 2019

Когда я обновляю существующую запись, Entity Framework up отправляет исходное значение в базу данных, но остальные поля работают нормально.

Сначала я использую Entity Framework 6 и хочу обновить несколько столбцов существующей записи, включая поля внешнего ключа. Сгенерированный скрипт обновления имеет все права, кроме одного значения внешнего ключа. Он продолжает использовать старое значение вместо нового, которое я назначаю.

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

Здесь задействованы три таблицы: Контракт, BillingCategory, Biller.
1. Контракт имеет много BillingCategories
2. Контракт имеет много биллеров
3. BillingCategory имеет много биллеров
4. Биллеру принадлежит один Контракт и одна BillingCategory


[TrackChanges]
public class Biller
{
    [Key]
    public int BillerId { get; set; }

    public int ContractId { get; set; }

    [ForeignKey("ContractId")]
    public virtual Contract Contract { get; set; }

    public int BillingCategoryId { get; set; }

    [ForeignKey("BillingCategoryId")]
    public virtual BillingCategory BillingCategory { get; set; }
}

[TrackChanges]
public class Contract
{
    public Contract()
    {
        BillingCategories = new HashSet<BillingCategory>();
    }

    [Key]
    public int ContractId { get; set; }

    public virtual ICollection<BillingCategory> BillingCategories { get; set; }
}

public class BillingCategory
{
    public BillingCategory()
    {
        Billers = new HashSet<Biller>();
    }

    [Key]
    public int BillingCategoryId { get; set; }

    public int ContractId { get; set; }

    [ForeignKey("ContractId")]
    public virtual Contract Contract { get; set; }

    public virtual ICollection<Biller> Billers { get; set; }
}

Я пробовал несколько способов изменить оба идентификатора, но когда я вызываю SaveChanges (), я вижу в сгенерированном скрипте BillerCategoryId - это его исходное значение, а не новое значение, которое я назначил.

  1. Изменить идентификатор
    List<Biller> modifiedBillers = _context.Billers.Where(m => m.ContractId == modified.ContractId).ToList();
    foreach (Biller b in modifiedBillers)
    {
        if (...)
        {
            b.ContractId = 23589;
            b.BillingCategoryId = 119662;
        }
    }

  1. Изменение идентификатора и свойства навигации
b.ContractId = 23589;
b.Contract = _context.Contracts.Where(m => m.ContractId == 23589).FirstOrDefault();

b.BillingCategoryId = 119662;
b.BillingCategory = _context.BillingCategories.Where(m => m.BillingCategoryId == 119662).FirstOrDefault();
  1. Изменить идентификатор и загрузить ссылку
b.ContractId = 23589;
_context.Entry(b).Reference(p => p.Contract).Load();

b.BillingCategoryId = 119662;
_context.Entry(b).Reference(p => p.BillingCategory).Load();

В SQL Profiler я вижу, что сгенерированный скрипт имеет неправильный BillCategoryId, но правильный ContractId, а остальные поля также правильные. Я подозреваю, что отношения между BillingCategory и Biller не объявлены должным образом.

exec sp_executesql N'UPDATE [dbo].[Billers] SET [ModificationStatus] = @0, [ContractId] = @1, [BillingCategoryId] = @2 WHERE ([BillerId] = @3)',N'@0 int,@1 int,@2 int,@3 int',@0=0,@1=23589,@2=119679,@3=250128

SaveChanges переопределяет DBContext SaveChanges, чтобы сделать некоторый контрольный журнал с использованием DBChangeTracker. В этом случае может помочь удаление журнала аудита, но я не вижу, как это влияет.

Однако даже в записях changeTracker BillingAllocationId как в OriginalValues, так и в CurrentValues ​​равняется 119679. Изменения в BillingAllocationId как-то не отслеживаются? Но это в скрипте обновления?

Я довольно потерян здесь. Любая помощь приветствуется.

Ниже приводится SaveChanges () с ведением журнала аудита.

    public override int SaveChanges()
    {
        Auditer?.AddEntityChangesForAuditing(EntityChanges, ChangeTracker);
        try
        {
            return base.SaveChanges();
        }      
        catch (Exception e)
        {
          ...
        }
    }

public class ContextChangeAuditer
{
    public void AddEntityChangesForAuditing(IDbSet<EntityChange> entityChanges, DbChangeTracker changeTracker)
    {
        string user = GetUserFromContext();
        var auditableEntities = GetAuditableEntities(changeTracker);
        foreach (var auditableEntity in auditableEntities)
        {
            var entityChange = CreateEntityChange(user, auditableEntity);
            entityChanges.Add(entityChange);
        }
    }

    private IEnumerable<DbEntityEntry> GetAuditableEntities(DbChangeTracker changeTracker)
    {
        var auditableEntities = changeTracker.Entries().Where(x => (x.State == EntityState.Added ||
        x.State == EntityState.Deleted ||
        x.State == EntityState.Modified) &&
        Attribute.GetCustomAttribute(x.Entity.GetType(),
        typeof(TrackChangesAttribute)) != null);

        return auditableEntities;
    }

    private EntityChange CreateEntityChange(string user, DbEntityEntry auditableEntity)
    {
        var entityChange = new EntityChange()
        {
            Date = DateTime.Now,
            User = user,
            Operation = auditableEntity.State,
            Entity = auditableEntity.Entity.GetType().Name,
            AccessType = DataAccessType.EntityFramework,
            RequestCorrelation = RequestTracingService.GetTransactionCorrelation(),
        };

        switch (auditableEntity.State)
        {
            case EntityState.Added:
                entityChange.NewValue = CreateWithValues(auditableEntity.CurrentValues);
                break;

            case EntityState.Deleted:
                entityChange.OldValue = CreateWithValues(auditableEntity.OriginalValues);
                break;

            case EntityState.Modified:
                entityChange.OldValue = CreateWithValues(auditableEntity.OriginalValues);
                entityChange.NewValue = CreateWithValues(auditableEntity.CurrentValues);
                break;
            default:

                throw new ArgumentOutOfRangeException();
        }

        return entityChange;
    }

    private string CreateWithValues(DbPropertyValues values)
    {
        var json = new JObject();
        foreach (var propertyName in values.PropertyNames)
        {
            json.Add(new JProperty(propertyName, values.GetValue < object(propertyName)));
        }
        return json.ToString();
    }
}

Обновление
Настройки в changeTracker._internalContext

AutoDetectChangesEnabled = true  
LazyLoadingEnabled = true  
ProxyCreationEnabled = true  
ValidateOnSaveEnabled = true  
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...