Entity Framework Core 복합 속성 업데이트 버전 차이
추천 자료: ASP.NET Core 인증 및 권한 부여
소개
Entity Framework Core는 .NET 애플리케이션에서 데이터베이스 작업을 간편하게 수행할 수 있도록 도와주는 ORM(Object-Relational Mapping) 프레임워크입니다. .NET 5.0과 .NET 8.0에서는 엔터티 업데이트를 처리하는 방식에 약간의 차이가 있습니다. 이 아티클에서는 두 버전의 차이점을 간단한 예제와 함께 설명합니다.
.NET 5.0 예제
.NET 5.0에서는 복합 속성(예: PhysicalAddress.Street
)을 처리하는 업데이트 메서드는 다음과 같이 작성할 수 있습니다:
public void Update(TEntity entity, params Expression<Func<TEntity, object>>[] properties)
{
var entry = dbContext.Attach(entity);
var originalValues = entry.GetDatabaseValues();
string userName = httpContextAccessor.HttpContext.User.Identity.Name;
DateTimeOffset currentTime = DateTimeOffset.Now;
long auditTrailId = 0;
foreach (var property in properties)
{
string propertyName = GetPropertyName(property);
if (propertyName.Contains(".") && propertyName.Split(".").Length == 2)
{
var referenceEntry = entry.Reference(propertyName.Split(".").First()).TargetEntry;
var originalValuesReference = referenceEntry.GetDatabaseValues();
var oldValue = originalValuesReference[propertyName.Split(".").Last()]?.ToString();
var newValue = referenceEntry.Property(propertyName.Split(".").Last()).CurrentValue?.ToString();
referenceEntry.Property(propertyName.Split(".").Last()).IsModified = oldValue != newValue;
}
else
{
var oldValue = originalValues[propertyName]?.ToString();
var newValue = entry.Property(propertyName).CurrentValue?.ToString();
entry.Property(propertyName).IsModified = oldValue != newValue;
}
// 감사 로직 생략
}
dbContext.SaveChanges();
}
private string GetPropertyName(Expression<Func<TEntity, object>> property)
{
return (property.Body as MemberExpression ?? ((UnaryExpression)property.Body).Operand as MemberExpression).Member.Name;
}
.NET 8.0 예제
.NET 8.0에서는 동일한 작업을 수행하기 위해 몇 가지 수정이 필요합니다. 복합 속성의 업데이트를 처리할 때 새로운 메서드를 사용하여 엔터티 속성 이름을 보다 안전하게 가져오고 처리해야 합니다.
public void Update(TEntity entity, params Expression<Func<TEntity, object>>[] properties)
{
var entry = dbContext.Attach(entity);
var originalValues = entry.GetDatabaseValues();
string userName = httpContextAccessor.HttpContext.User.Identity.Name;
DateTimeOffset currentTime = DateTimeOffset.Now;
long auditTrailId = 0;
foreach (var property in properties)
{
string propertyName = GetPropertyName(property);
if (propertyName.Contains("."))
{
var propertyNames = propertyName.Split('.');
var referenceEntry = entry.Reference(propertyNames.First()).TargetEntry;
var originalValuesReference = referenceEntry.GetDatabaseValues();
var oldValue = originalValuesReference != null && originalValuesReference[propertyNames.Last()] != null
? originalValuesReference[propertyNames.Last()].ToString()
: string.Empty;
var newValue = referenceEntry.Property(propertyNames.Last()).CurrentValue?.ToString() ?? string.Empty;
referenceEntry.Property(propertyNames.Last()).IsModified = !newValue.Equals(oldValue);
if (!newValue.Equals(oldValue))
{
HandleAuditTrail(entity, userName, currentTime, ref auditTrailId, propertyName, oldValue, newValue);
}
}
else
{
var oldValue = originalValues != null && originalValues[propertyName] != null
? originalValues[propertyName].ToString()
: string.Empty;
var newValue = entry.Property(propertyName).CurrentValue?.ToString() ?? string.Empty;
entry.Property(propertyName).IsModified = !newValue.Equals(oldValue);
if (!newValue.Equals(oldValue))
{
HandleAuditTrail(entity, userName, currentTime, ref auditTrailId, propertyName, oldValue, newValue);
}
}
}
dbContext.SaveChanges();
}
private string GetPropertyName(Expression<Func<TEntity, object>> property)
{
var memberExpression = property.Body as MemberExpression ?? ((UnaryExpression)property.Body).Operand as MemberExpression;
return memberExpression.Member.Name;
}
private void HandleAuditTrail(TEntity entity, string userName, DateTimeOffset currentTime, ref long auditTrailId, string propertyName, string oldValue, string newValue)
{
try
{
if (auditTrailId == 0)
{
var entityDetails = GetEntityDetails(entity, entity.GetType().Name, entity.ID);
auditTrailId = AddAuditTrailRecord(entity.GetType().Name, entity, userName, currentTime, entityDetails, "Update");
}
if (auditTrailId == -1)
{
return;
}
var auditTrailChange = new AuditTrailChange
{
PropertyName = propertyName,
OldValue = oldValue,
NewValue = newValue,
AuditTrailRecordID = auditTrailId,
CreatedBy = userName,
CreatedAt = currentTime,
Active = true // Ensure the record is active
};
dbContext.Add(auditTrailChange);
}
catch (Exception ex)
{
// Log the exception or handle it as needed
Console.WriteLine($"Error in HandleAuditTrail: {ex.Message}");
}
}
주요 차이점
- 속성 이름 추출: .NET 5.0에서는
GetPropertyName
메서드를 사용하여 속성 이름을 추출하는 데 문제가 없었지만, .NET 8.0에서는 복합 속성 이름을 보다 안전하게 처리하기 위해 로직을 개선해야 합니다. - 복합 속성 처리: .NET 8.0에서는 복합 속성을 보다 명확하게 처리하고
TargetEntry
를 활용하여 참조 엔터티의 값을 올바르게 가져오고 설정합니다. - 감사 로직 처리: 새로운
HandleAuditTrail
메서드를 도입하여 감사 로직을 분리하고, 감사 기록을 보다 명확하게 관리합니다.
결론
.NET 5.0과 .NET 8.0 모두 Entity Framework Core를 사용하여 엔터티를 업데이트할 수 있지만, 버전 간의 미세한 차이로 인해 코드의 일부를 수정해야 할 수 있습니다. 이 아티클에서는 이러한 차이점을 이해하고 최신 버전에서 발생할 수 있는 문제를 해결하는 방법을 배웠습니다.
추천 자료: .NET Blazor에 대해 알아보시겠어요? .NET Blazor 알아보기를 확인해보세요!