1

Entity Framework 응용 프로그램에 거의 동시 입력을 처리하려고합니다. 회원 (사용자)은 평가할 수 있기 때문에 등급에 대한 표가 있습니다. 한 열은 회원의 ID이고, 하나는 등급을 매기는 사람의 ID이고, 하나는 등급이고, 다른 하나는 등급을 매기는 시간입니다. . 가장 최근 등급은 이전 등급보다 우선합니다. 입력을 받으면 멤버가 이미 등급을 매겼는지 여부를 확인한 후, 기존 행을 사용하여 등급을 업데이트하거나 그렇지 않은 경우 새로운 행을 추가합니다. 나는 거의 동일한 시간에 동일한 항목에 대해 동일한 사용자로부터 입력이 들어올 때 동일한 일에 대해 해당 사용자에 대해 두 가지 등급으로 끝난다는 것을 알아 챘다.삽입하지 못한 후 행을 업데이트하기 위해 고유 제한 조건 예외를 처리하는 방법은 무엇입니까?

이전에 나는 How can I avoid duplicate rows from near-simultaneous SQL adds?을 물었고, MemberID와 ThingID의 고유 한 조합을 요구하는 SQL 제약 조건을 추가하기위한 제안을 따랐지만이 기법을 작동시키는 데 어려움을 겪고있다. 예외가 발생할 때 내가하고 싶은 일을하기위한 문법을 ​​알아라. 예외는 제약 조건이 위반되었다는 것을 알게되고, 그때 내가하고 싶은 것은 같은 MemberID와 ThingID를 가진 행의 불법 추가 시도를 잊어 버리고 대신 기존의 것을 가져 와서이 값을 약간 더 최근의 값으로 설정합니다 데이터. 그러나 나는 그렇게 할 구문을 생각해 낼 수 없었다. 몇 가지 시도하고 예외를 얻은 후 SaveChanges 시도 할 때 예외가 발생합니다 - 고유 제약 조건이 계속 올라오고 있거나 교착 상태 예외가 발생합니다.

내가 노력 최신 버전은 다음과 같이이었다, 나는 또한에서 범위를 떠날 때 엔티티 프레임 워크는 데이터베이스 트랜잭션을 정리 것처럼 보이기 때문에, 함수 호출에서 논리를 포장하려

// Get the member's rating for the thing, or create it. 
Member_Thing_Rating memPref = (from mip in _myEntities.Member_Thing_Rating 
           where mip.thingID == thingId 
           where mip.MemberID == memberId 
           select mip).FirstOrDefault(); 
bool RetryGet = false; 
if (memPref == null) 
{ 
    using (TransactionScope txScope = new TransactionScope()) 
    { 
     try 
     { 
      memPref = new Member_Thing_Rating(); 
      memPref.MemberID = memberId; 
      memPref.thingID = thingId; 
      memPref.EffectiveDate = DateTime.Now; 
      _myEntities.Member_Thing_Rating.AddObject(memPref); 
      _myEntities.SaveChanges(); 
     } 
     catch (Exception ex) 
     { 
      Thread.Sleep(750); 
      RetryGet = true; 
     } 
    } 
    if (RetryGet == true) 
    { 
     Member_Thing_Rating memPref = (from mip in _myEntities.Member_Thing_Rating 
             where mip.thingID == thingId 
             where mip.MemberID == memberId 
             select mip).FirstOrDefault(); 
    } 
} 

위를 기록 후 변경 사항이 제출되었습니다. 고유 제한에 대한 예외의 끝없는 문자열에

bool Succeeded = false; 
while (Succeeded == false) 
{ 
    Thread.Sleep(750); 
    Exception Problem = AttemptToSaveMemberIngredientPreference(memberId, ingredientId, rating); 
    if (Problem == null) 
     Succeeded = true; 
    else 
    { 
     Exception BaseEx = Problem.GetBaseException(); 
    } 
} 

그러나이 결과 만 : 그래서 그 대신하여 TransactionScope를 사용하여 위와 동일한 수준에서 예외를 관리,이처럼 관리 기능 내부 전체를 감싸 높은 수준의 기능에서 영원히 처리됩니다. 나는 시도 사이에 3/4 초의 지연이 있기 때문에,보고 된 충돌이있을 수 있지만 여전히 행을 쿼리 할 때 아무 것도 발견되지 않는다는 것에 놀랐습니다. 모든 스레드가 동시에 실행되기 때문에 모든 스레드가 실패하고 Entity Framework가이를 모두 알아 차리고 성공하기 전에 모두 실패했음을 나타냅니다. 따라서 모든 제출물을보고 조정하여 예외에 대응할 수있는 방법이 있어야한다고 생각합니다. 나는 그것에 대한 구문을 모르거나 보지 못했다. 다시 한번, 이것을 처리하는 방법은 무엇입니까?

업데이트 : 패디는 아래에 세 가지 좋은 제안을합니다. 그의 Stored Procedure 기술이이 문제를 해결할 것으로 기대하지만 여전히 질문에 대한 답변에 관심이 있습니다. 즉, 반드시 제출을 조작하여이 예외에 응답 할 수 있어야하지만 아직 한 행을 삽입하고 최신 값을 사용하는 구문을 찾지 못했습니다.

답변

1

에릭 리 퍼트 (Eric Lippert)의 말을 인용하자면, "상처를 입으면 그 일을 그만 두십시오." 매우 높은 volumnes를 기대하고 있고 '삽입 또는 업데이트'를 원할 경우 위에 설명 된 방법 대신 저장 프로 시저 내에서 처리하는 것이 좋습니다.

존재 여부와 삽입/업데이트를 확인하기 위해 DB 호출 사이에 약간의 차이가 있기 때문에 문제가 발생합니다.

sproc은 테이블의 한 번에 삽입 또는 업데이트를 수행하기 위해 MERGE를 사용하여 등급에 대해 단일 행만 표시하고 가장 최근에 받게 될 업데이트를 보장합니다.


주 - EF 모델에 sproc을 포함시키고 유사한 EF 구문을 사용하여 호출 할 수 있습니다.


참고 2 - 코드를 보면 예외가 발생한 경우 스레드를 대기 상태로두기 전에 트랜잭션 범위를 롤백하지 않습니다. 특히 거래량이 매우 많을 때 트랜잭션을 열어 두는 데 오랜 시간이 걸립니다. 이 같은 코드 뭔가를 업데이트 할 수 있습니다 : 당신은 당신이 시도 할 때 교착 상태 앓고있는 것 같다 이유

try 
    { 
     memPref = new Member_Thing_Rating(); 
     memPref.MemberID = memberId; 
     memPref.thingID = thingId; 
     memPref.EffectiveDate = DateTime.Now; 
     _myEntities.Member_Thing_Rating.AddObject(memPref); 
     _myEntities.SaveChanges(); 
     txScope.Complete(); 
    } 
    catch (Exception ex) 
    { 
     txScope.Dispose(); 
     Thread.Sleep(750); 
     RetryGet = true; 
    } 

이있을 수 있습니다, 당신은 빠른 동시 요청을 받고 특히합니다.

+0

감사합니다. 현명한 단어. 나는 이미 상황을 다루는 다른 방법을 생각해 왔지만, 내가 이렇게하는 방법을 제안했기 때문에 이것을하는 방법이 무엇인지 궁금하다. 일반적으로 중단하지 않고 예외를 처리하는 방법을 알고있는 것이 일반적으로 유용합니다. 하지만 네, 뮤텍스를 사용하거나 여러 행을 허용하고 나중에 정리하면 나에게 도움이 될 것이라고 생각합니다. – Dronz

+0

메모 2를 추가해 주셔서 감사합니다. 나는 그것을 밖으로 시도 할 것입니다 ... – Dronz

+0

아주 좋은 지적이었습니다. 그래서 그 문법에 대해 감사드립니다.하지만 제안한대로 테스트했을 때, 나는 여전히 고유 한 제약 조건 예외를 계속 반복해서 시도합니다. 다시 제출하십시오. 따라서 Dispose()는 예외가 발생한 제출을 잊어 버린 것이 아니며 함수 호출 수준도 올라가지 않습니다. 보류중인 변경 사항을 얻고 조작하는 방법이 있어야합니다 (LINQ를 통해 수행했습니다).하지만 먼저 말했듯이 제약 조건을 취소하고 코드에서 추가 행을 처리하는 것이 더 쉬워 보입니다. – Dronz