이전 기사에서는 업데이트 작업에 LINQ to SQL을 사용할 때 발생할 수 있는 몇 가지 문제를 소개했습니다. 사실 이것은 저 혼자 겪은 문제가 아닙니다. 인터넷에서 답변을 찾아보니 이 주제에 관해 비슷한 글을 게시한 사람들이 많이 있었습니다. 하지만 만족스럽지 못한 점은 문제를 제기하면서도 상세한 분석을 실시하지 않았고, 해결책(RowVersion 열 추가, 연결 제거 등)만 제시했을 뿐 왜 이렇게 해야 하는지 설명하지 않았다는 점입니다. . 이는 이전 글을 작성한 본래 의도이기도 한데, LINQ to SQL 소스코드 분석을 통해 차근차근 문제의 해결방안을 찾아보고자 한다. 이 기사에서는 이러한 방법을 하나씩 설명합니다.
옵션 1: 재할당 TerryLee, Anytao, Ding Xue 등의 오픈 소스 프레임워크 Ezsocio에서는 일부 위치에서 재할당이 채택됩니다. Update 메서드 내에서 기본 키를 기반으로 데이터베이스의 엔터티를 얻은 다음 매개 변수의 엔터티를 사용하여 해당 속성에 값을 하나씩 할당합니다.
공개 무효 UpdateProfile(프로필 p)
{
(RepositoryContext db = 새로운 RepositoryContext()) 사용
{
var profile = db.GetTable<Profile>().First<Profile>(u => u.ID == p.ID);
profile.Birthday = p.생일;
profile.Gender = p.성별;
profile.Hometown = p.고향;
profile.MSN = p.MSN;
profile.NickName = p.닉네임;
profile.PhoneNumber = p.PhoneNumber;
profile.QQ = p.QQ;
profile.State = p.State;
profile.TrueName = p.TrueName;
profile.StateRefreshTime = p.StateRefreshTime;
profile.Avatar = p.Avatar;
profile.Website = p.웹사이트;
db.제출변경사항();
}
}
Yang Guo 형제는 또한 속성 값의 자동 복사를 달성하기 위해 이 솔루션에 대한 반영 방법을 제공했습니다.
하지만 개인적으로 이는 현실을 회피하고 업데이트 작업을 위해 LINQ to SQL에서 제공하는 API를 사용하지 않고 우회 전략을 채택하는 방식이라고 생각합니다. 이것은 실제로 절충안입니다. Attach 메서드가 "사용하기 쉽지 않아" 사용하지 않기 때문입니까? 헤헤.
옵션 2: 객체 추적 비활성화 이와 관련하여 lea는 DataContext의 ObjectTrackingEnabled 속성을 false로 설정하면 올바른 업데이트를 얻을 수 있다고 제안했습니다.
공개 제품 GetProduct(int id)
{
NorthwindDataContext db = new NorthwindDataContext();
db.ObjectTrackingEnabled = 거짓;
return db.Products.SingleOrDefault(p => p.ProductID == id);
}
다른 코드는 변경되지 않습니다.
객체 추적을 비활성화한 후에도 정상적으로 업데이트할 수 있는 이유는 무엇입니까? 소스코드에서 답을 찾아보자.
public bool ObjectTrackingEnabled
{
얻다
{
this.CheckDispose();
this.objectTrackingEnabled를 반환합니다.
}
세트
{
this.CheckDispose();
if(this.Services.HasCachedObjects)
{
throw System.Data.Linq.Error.OptionsCannotBeModifiedAfterQuery();
}
this.objectTrackingEnabled = 값;
if (!this.objectTrackingEnabled)
{
this.deferredLoadingEnabled = 거짓;
}
this.services.ResetServices();
}
}
ObjectTrackingEnabled가 false로 설정되면 DeferredLoadingEnabled도 동시에 false로 설정되는 것으로 나타났습니다. 이러한 방식으로 쿼리를 실행할 때 지연된 쿼리가 필요한 데이터가 엔터티에 대해 로드되지 않으므로 연결 중에 예외가 발생하지 않습니다(이전 문서의 분석 참조).
MSDN에서는 다음과 같은 유용한 정보도 얻을 수 있습니다. ObjectTrackingEnable 속성을 false로 설정하면 추적할 항목 수가 줄어들기 때문에 검색 성능이 향상될 수 있습니다. 이것은 매우 유혹적인 기능입니다.
그러나 객체 추적을 비활성화하는 경우 다음 두 가지 사항에 특별한 주의를 기울여야 합니다. (1) 쿼리를 실행하기 전에 비활성화해야 합니다. (2) 비활성화된 후에는 Attach 및 SubmitChanges 메서드를 더 이상 호출할 수 없습니다. 그렇지 않으면 예외가 발생합니다.
옵션 3: 연결 제거 이전 기사에서는 GetProduct 메서드에서 Product와 연결된 Category를 수동으로 null로 설정하는 잘못된 메서드를 도입했습니다. 코드의 이 부분을 추출하여 Detach 메서드에 넣을 수 있습니다. 이 Detach는 엔터티 메서드이므로 부분 클래스를 사용할 수 있습니다.
공개 부분 클래스 제품
{
공개 무효 분리()
{
this._Category = default(EntityRef<카테고리>);
}
}
공개 부분 클래스 카테고리
{
공개 무효 분리()
{
foreach(this.Products의 var 제품)
{
제품.분리();
}
}
}그러나 각 엔터티에 대해 Detach를 정의하는 이 방법은 너무 번거롭습니다. 엔터티 수가 증가하면 관계가 점점 더 복잡해지고 누락된 속성이 나타나기 쉽습니다. Zhang Yi는 리플렉션을 사용하여 이 논리를 추상화하는 매우 우아한 방법을 제안했습니다.
private void Detach(TEntity 엔터티)
{
foreach(entity.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Instance)의 FieldInfo fi))
{
if (fi.FieldType.ToString().Contains("EntityRef"))
{
var value = fi.GetValue(entity);
if (값 != null)
{
fi.SetValue(entity, null);
}
}
if (fi.FieldType.ToString().Contains("EntitySet"))
{
var value = fi.GetValue(entity);
if (값 != null)
{
MethodInfo mi = value.GetType().GetMethod("지우기");
if (mi != null)
{
mi.Invoke(값, null);
}
fi.SetValue(엔티티, 값);
}
}
}
}
Detach 중에 PropertyChanging 및 PropertyChanged 이벤트를 null로 설정해야 한다고 생각하는 사람들도 있지만 전체적인 아이디어는 동일합니다.
옵션 4: 위임 사용 이것은 내 지난 기사의 댓글에서 ZC29가 제공한 방법입니다. 개인적으로 배울 가치가 있다고 생각합니다.
공개 무효 UpdateProductWithDelegate(Expression<Func<Product, bool>> 조건자, Action<Product> 작업)
{
NorthwindDataContext db = new NorthwindDataContext();
var product = db.Products.SingleOrDefault(술어);
행동(제품);
db.제출변경사항();
}
//클라이언트 코드
ProductRepository 저장소 = 새 ProductRepository();
저장소.UpdateProductWithDelegate(p => p.ProductID == 1, p =>
{
p.ProductName = "변경됨";
});
Lambda 표현식을 사용하여 GetProduct 논리를 UpdateProduct에 포함하고 대리자를 사용하여 업데이트 논리 실행을 연기합니다. 이렇게 하면 검색 및 업데이트가 DataContext에 교묘하게 배치되므로 Attach가 우회됩니다. 그러나 이 방법의 API는 약간 복잡하고 클라이언트 프로그래머의 수준이 너무 높습니다. 게다가 업데이트에서 Get 로직을 다시 실행해야 하기 때문에 성능 손실은 미미하지만 항상 사람들에게 DRY가 부족하다는 느낌을 주는 것 같습니다.
옵션 5: UPDATE 문 사용 Ezsocio의 소스 코드에서 RepositoryBase.UpdateEntity 메서드를 찾았습니다. SQL문의 스플라이싱은 메소드 내에서 이루어지며, 변경된 컬럼만 업데이트됩니다. 여기서는 ITable이 더 이상 사용되지 않고 전체 프레임워크 지원이 필요하므로 더 이상 설명하지 않습니다. 자세한 내용은 Ezsocio의 소스코드를 참고하세요.
요약 이 기사에는 제가 최근 인터넷에서 찾은 몇 가지 솔루션이 나열되어 있습니다. 이들 솔루션에는 모두 장단점이 있습니다. 어느 것이 더 좋고 더 나쁜지는 다른 사람들의 의견에 따라 다릅니다. 다음 기사에서는 최적의 솔루션을 찾기 위해 이러한 방법의 성능을 비교하겠습니다.