2011-11-30 2 views
1

다음 시나리오는 꽤 일반적이라고 말하지만 해결 방법은 하나 있지만 우아함이 부족합니다.NHibernate 업데이트 전에 dB에 대한 유효성 검사를 수행하는 방법

내가 제공하는 예제는 https://github.com/sharparchitecture/Sharp-Architecture-Cookbook을 기반으로합니다.

코딩중인 응용 프로그램은 ASP.NET MVC 응용 프로그램이며 같은 개체에서 작업하는 여러 사용자를 지원해야합니다.

다음 시나리오는 딱딱한 케이스이지만 올바른 것입니다.

동일한 개체에서 작업하는 두 명의 사용자가 있고 dB 행을 업데이트 할 수 있는지 여부는 특정 필드의 값에 따라 다릅니다. 보다 구체적으로 말하자면, 제품이 있고 단순하게 유지한다고 가정 할 때이 제품에는 "이름"과 "QuantityInStock"필드가 있습니다.

처음에는 제품 및 사용자 1이 10 개 있고 사용자 1과 사용자 2가이 제품을 구매하려고합니다. 두 사용자 모두 초기 양식을 제출하면 재고가 10 개 있다고 알려줍니다. 이제 User1은 10 개의 항목을 모두 구매하고 User2는 커피를 갖습니다. 따라서 User1의 트랜잭션은 문제없이 진행됩니다.

그런 다음 User2는 재고가 여전히 10 개 있다고 생각하여 커피를 마신 후 다시 방문합니다. 그래서 그는 1을 사려고하지만 재고가 없기 때문에 그렇게하지 못하게해야합니다.

이 문제는 ASP.NET DataAnnotations 유효성 검사를 사용하여 해결할 수 있으며 대부분의 경우를 파악합니다. 그러나 User1과 User2가 동일한 작업을 수행하지만 User2가 양식을 제출할 때 ASP.NET 유효성 검사를 통과하지만 지속성 계층에 도달 할 때까지는 1 초도 채 걸리지 않는다고합니다. QuantityInStock은 0입니다.

가능한 해결책은 Update 메서드를 호출하기 바로 전에 가능한 한 최신 시점에 유효성 검사를 수행하는 것입니다.

일부 코드입니다. 이제

public ProductModel CreateOrUpdate(ProductModel productModel) 
{ 
    var currentProductModel = Get(productModel.Id); 

    var currentQuantityInStock = currentProductModel.QuantityInStock; 


    if(currentProductModel.QuantityInStock !=0 && productModel.QuantityInStock >= currentQuantityInStock) 
    { 
     currentProductModel.QuantityInStock= productModel.QuantityInStock; 
     currentProductModel.Name = productModel.Name; 

     this.productModelRepository.SaveOrUpdate(currentProductModel); 
     return productModel; 
    } 
    else 
    { 
     //Raise an exception 
    } 
} 

, 내가 전화 한 사실 :

:

this.productModelRepository.SaveOrUpdate(productModel); 

이 예외가 발생합니다 :

var currentProductModel = Get(productModel.Id); 

난 그냥이 작업을 수행하는 경우 있음을 의미

동일한 식별자 값을 가진 다른 객체가 이미 세션 : 1

따라서 모든 값을 productModel에서 currentProductModel로 복사해야합니다. Automapper와 같은 것을 사용할 때도 괜찮지 만, 여전히 한 종류의 객체에서 다른 객체로 데이터를 전송하지 않고도 productModel을 그대로 저장할 수 있어야한다는 느낌으로 나에게 잘못된 느낌을줍니다.

또한 동일한 유효성 검사를 두 번 수행하면 DataAnnotation을 사용한 후 한 번만 업데이트하기 직전에 DRY 원칙이 위반됩니다.

중요한 점은 트릭을 놓치고있는 것 같지만 시작점과 조사 대상을 잘 모르는 것입니다.

이것은 나에게있어 간단한 문제이지만 멋진 해결책을 제시하는 것은 다른 것입니다. 그래서 문제는 과거에이 단순한 사건을 어떻게 다루었습니까? 나는 이것을 overthinking인가?

답변

0

낙천적으로 버전 잠금을 시도해 보셨나요?

// Fluent mapping 
public EntitiyMap() 
{ 
    OptimisticLock.All(); // all properties musn't be changed in db when saving 
    // or 
    OptimisticLock.Dirty(); // only dirty properties musn't be changed in db when saving 
} 


// 
public ProductModel CreateOrUpdate(ProductModel productModel) 
{ 
    try 
    { 
     // productModel is already validated and updated 
     this.productModelRepository.SaveOrUpdate(productModel); 

     return productModel; 
    } 
    catch (StaleObjectException) 
    { 
     // somebody changed the object in database after we have read it 
     // Raise an exception or whatever 
    } 
} 

업데이트 : 아무도 그것을 사이에 변경하지 않는 경우 다른 방법으로

public void BuySomething(ProductModel productModel, int amount) 
{ 
    int tries = 5; 
    bool success = false; 
    while(!success && tries > 0) 
    { 
     if (productModel.QuantityInStock <= amount) 
     { 
      //Raise an exception 
     } 

     productModel.QuantityInStock - amount; 
     try 
     { 
      this.productModelRepository.SaveOrUpdate(productModel); 
     } 
     catch (StaleObjectException) 
     { 
      // somebody changed the object in database after we have read it 
      this.productModelRepository.Refresh(productModel); 
      tries--; 
     } 
    } 
    if (tries <= 0) 
    { 
     // Raise an exception or whatever 
    } 
} 

제로 추가 왕복 그런 일을 처리하고,

+0

예 이것은 물론 작동 거래의 직렬화를 보장. 그러나 시나리오는 User1이 5를 사며 User2가 1을 구입하고이 경우 StaleObjectException을 트리거하지 않을 수도 있습니다. – DavidS

+0

왜 staleobject를 트리거하지 않으면 DB에 수량 5 또는 9가 있지만 실제로 4일까요? – Firo

+0

"CreateOrUpdate"에 대한 코드가 명확하지 않을 수 있습니다. 업데이트를 적용하려면 "if"조건의 업데이트를 참조하십시오. 많은 수의 거래가있는 웹 사이트를 상상해보십시오. 재고가있는 상품이 있지만 StaleObjectException이 발생하면 사용자는 주문을 다시 제출하기가 쉽지 않습니다. – DavidS

관련 문제