Когда я обновляю существующую запись, 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 - это его исходное значение, а не новое значение, которое я назначил.
- Изменить идентификатор
List<Biller> modifiedBillers = _context.Billers.Where(m => m.ContractId == modified.ContractId).ToList();
foreach (Biller b in modifiedBillers)
{
if (...)
{
b.ContractId = 23589;
b.BillingCategoryId = 119662;
}
}
- Изменение идентификатора и свойства навигации
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();
- Изменить идентификатор и загрузить ссылку
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