2009-09-30 2 views
6

Linq 캐싱 데이터 값 - 주요 동시성 문제?

다음은 약간의 실험입니다.

MyClass obj = dataContext.GetTable<MyClass>().Where(x => x.ID = 1).Single(); 
Console.WriteLine(obj.MyProperty); // output = "initial" 
Console.WriteLine("Waiting..."); // put a breakpoint after this line 
obj = null; 
obj = dataContext.GetTable<MyClass>().Where(x => x.ID = 1).Single(); // same as before, but reloaded 
Console.WriteLine(obj.MyProperty); // output still = "initial" 
obj.MyOtherProperty = "foo"; 
dataContext.SubmitChanges(); // throws concurrency exception 

3 번 줄 다음에 중단 점에 도달하면 SQL 쿼리 창으로 이동하여 수동으로 값을 "updated"로 변경합니다. 그런 다음 나는 계속 달리다. Linq는 객체를 다시로드하지 않지만 이전에 메모리에 있던 객체를 다시 사용합니다! 이것은 데이터 동시성을위한 거대한 문제입니다!

어떻게 Linq 분명히 메모리에 보관됩니다 개체의 숨겨진 캐시를 해제합니까?

EDIT - 리플렉션에서 Microsoft는 Linq 프레임 워크에서 그런 갈라지는 틈을 남겨 둡니다. 위의 코드는 내가 실제로하는 일의 어리석은 버전이며 내가 놓친 작은 미묘한 부분이있을 수 있습니다. 요컨대, 위의 결과가 맞는지 확인하기 위해 자신 만의 실험을한다면 감사하게 생각합니다. 또는 Linq를 동시 데이터 업데이트에 대해 견고하게 만드는 일종의 "비밀 스위치"가 있어야합니다. 근데 뭐?

답변

5

이것은 내가 전에 건너 한 문제가되지 않습니다 있습니다

http://www.rocksthoughts.com/blog/archive/2008/01/14/linq-to-sql-caching-gotcha.aspx

+1

+1 OMG, 정말 Gotcha입니다! C# Gotcha KB에이를 추가해야합니다 ... –

+1

거기에 그의 조언을 따르고 ObjectTrackingEnabled = false로 설정하면 더욱 심하게 DB에서 읽을 수 있습니다. SubmitChanges를 할 수 없습니다! 그것은 정말로 삶을 어렵게 만듭니다! –

+0

다음은 C# gotcha KB에 대한 링크입니다. http://stackoverflow.com/questions/241134/what-is-the-worst-c-net-gotcha –

2

DataContext의 ObjectTrackingEnabled 속성을 false로 설정하십시오.

ObjectTrackingEnabled를 true로 설정하면 DataContext는 Unit of Work처럼 동작합니다. 그것은 메모리에로드 된 객체를 유지하여 변경 사항을 추적 할 수있게합니다. DataContext는 원래 변경된 사항을 알기 위해 객체를로드 할 때 객체를 기억해야합니다.

읽기 전용 시나리오에서 작업하는 경우 개체 추적을 해제해야합니다. 상당한 성능 개선이 될 수 있습니다.

읽기 전용 시나리오에서 작업하지 않는 경우 왜 이런 방식으로 작동 시키려하는지 확신 할 수 없습니다. 편집을 한 경우 데이터베이스에서 수정 된 상태로 가져 오려는 이유는 무엇입니까?

+0

예.하지만 읽기 전용 환경에서는 작동하지 않습니다. 다른 아이디어? –

+0

이것은 의도적으로 설계된 동작입니다. optamistic 동시성을 사용하여 상태를 가져오고, 수정하고 다시 쓰도록 설계되었습니다. DataContext에서 새로 고침을 호출 할 수는 있지만 변경 사항은 삭제 될 수 있습니다. 실시간 동기화를 제공하기위한 것이 아닙니다. 실시간 동기화 및 편집은 잘 진행되지 않습니다. –

1

LINQ to SQL은 ID 맵 디자인 패턴을 사용합니다. 즉, 개체 추적을 해제하지 않는 한 기본 키가 지정된 개체의 동일한 인스턴스를 항상 반환합니다.

해결 방법은 첫 번째 인스턴스를 방해하거나 첫 번째 인스턴스를 새로 고치지 않으려면 두 번째 데이터 컨텍스트를 사용하는 것입니다.

5

LinqToSql에는 동시성 문제를 처리 할 수있는 다양한 도구가 있습니다.

그러나 첫 번째 단계는 해결할 동시성 문제가 있음을 인정하는 것입니다.

먼저 DataContext의 의도 된 개체 수명주기는 UnitOfWork와 일치해야합니다. 오랜 기간 동안 하나를 붙들고 있다면, 클래스가 그런 식으로 사용되도록 설계되지 않았기 때문에 훨씬 더 열심히해야 할 것입니다.

둘째, DataContext는 각 개체의 두 복사본을 추적합니다. 하나는 원래 상태이고 하나는 변경/변경 가능 상태입니다. Id = 1을 가진 MyClass를 요청하면 동일한 인스턴스을 제공합니다. 마지막으로 변경되었거나 변경 될 수있는 버전입니다. 원본이 아닙니다.LinqToSql은 하나의 DataContext가 두 개의 변경 가능한 버전의 MyClass (Id = 1)를 인식하는 것을 허용하지 않습니다.

셋째, DataContext는 메모리 변경이 데이터베이스 변경 전후인지 여부를 알지 못하므로 일부 지침없이 동시성 충돌을 판단 할 수 없습니다. 보이는 내용은 다음과 같습니다.

  • 데이터베이스에서 MyClass (Id = 1)를 읽었습니다.
  • 프로그래머가 MyClass (Id = 1)를 수정했습니다.
  • 내가 다시 데이터베이스에 MyClass에 (ID = 1) 전송
    • 데이터베이스의 버전은 원래의 (낙관적 동시성을) 일치하는 경우 업데이트가 성공 (where 절에서 낙관적 동시성을 보려면이 SQL보고).
    • 데이터베이스의 버전이 원본과 일치하지 않으면 업데이트가 동시성 예외로 인해 실패합니다.

좋아, 지금 문제가 언급되어, 여기에 대처하는 몇 가지 방법입니다.

DataContext를 버리고 처음부터 다시 시작할 수 있습니다. 이것은 약간 무거운 물건이지만 적어도 구현하기 쉽습니다.

(reference docs with many good concurrency links in the "Remarks" section)을 호출하여 원본 인스턴스 또는 변경/변경 가능 인스턴스를 데이터베이스 값으로 새로 고치도록 요청할 수 있습니다. 이렇게하면 클라이언트 측에서 변경 사항이 발생하고 최종 결과가 나오도록 코드를 수정할 수 있습니다.

dbml (ColumnAttribute.UpdateCheck)에서 동시성 검사를 해제 할 수 있습니다. 이렇게하면 낙관적 인 동시성이 불가능 해지고 코드가 다른 사람의 변경 내용을 스톰프하게됩니다. 또한 무거운 손으로 구현하기 쉽습니다.