2017-04-24 84 views
1

我寫了一個快速的c#擴展方法,但不知道是否有更乾淨的方式來完成我想要做的事情。它確實有效,但是使用字符串轉發器感覺有點冒險,並插入一個小數。C#/ TSQL小數邊界檢查 - 有更清晰的方法嗎?

目標是在應用程序級別,我們可以在發送到數據庫之前清除/修復任何數據問題以防止溢出。

注意:PCL庫,在這種情況下無法引用DLL的外部。

public static bool TsqlDecimalBoundariesCheck(this decimal valueToCheck, int precision, int scale) 
    { 
     if(scale > precision) throw new ArgumentException($"BOUNDARY CHECK: Scale [{scale}] must not be higher than Percision [{precision}]"); 

     // create X precision values of the value 9 
     var precisionValue = new string('9', precision); 

     // Insert the decimal place x positions from the right 
     if (scale > 0) 
     { 
      precisionValue = precisionValue.Insert((precision - scale), "."); 
     } 

     // Get the upper and lower values 
     var upperBoundry = decimal.Parse(precisionValue); 
     var lowerBoundry = upperBoundry * -1; 

     return (valueToCheck <= upperBoundry) && (valueToCheck >= lowerBoundry); 
    } 

而且一些快速的單元測試陪它:

[TestMethod] 
    public void TestBoundryConstraints() 
    { 
     var precision = 4; 
     var scale = 1; 

     var testValue = 1000m; 
     var result = testValue.TsqlDecimalBoundariesCheck(precision , scale); 
     Assert.IsFalse(result, $"Value {testValue} is expected to be outside Decimal({precision }, {scale})"); 

     testValue = -1000m; 
     result = testValue.TsqlDecimalBoundariesCheck(precision , scale); 
     Assert.IsFalse(result, $"Value {testValue} is expected to be outside Decimal({precision }, {scale})"); 

     testValue = 100m; 
     result = testValue.TsqlDecimalBoundariesCheck(precision , scale); 
     Assert.IsTrue(result, $"Value {testValue} is expected to be within Decimal({precision }, {scale})"); 

     testValue = 999.9m; 
     result = testValue.TsqlDecimalBoundariesCheck(precision , scale); 
     Assert.IsTrue(result, $"Value {testValue} is expected to be within Decimal({precision }, {scale})"); 

     testValue = -999.9m; 
     result = testValue.TsqlDecimalBoundariesCheck(precision , scale); 
     Assert.IsTrue(result, $"Value {testValue} is expected to be within Decimal({precision }, {scale})"); 
    } 
+3

的'1000m'測試是重複的,你typo'd'precision'一致。 :) – xxbbcc

+0

請參閱http://stackoverflow.com/a/4438489/714151 – MrZander

+0

謝謝..是的,可怕的拼寫..因爲大多數開發人員,據我瞭解。謝謝你的收穫。爲什麼VS還沒有添加拼寫檢查;) – TravisWhidden

回答

0

所以你絕對可以擺脫哈克字符串做(10^p - 1) * (10^-s),讓您的上限和下限重複。

如果您想檢查以確保縮放不會被截斷,您可以實際上截斷它,然後比較值。如果截斷值和原始值相同,則比例有效。

全部放在一起,你會得到這樣的事情:

public static bool TsqlDecimalBoundariesCheck(this decimal valueToCheck, int precision, int scale) 
{ 
    if (scale > precision) throw new ArgumentException($"BOUNDARY CHECK: Scale [{scale}] must not be higher than Percision [{precision}]"); 

    //Upper/lower bounds 
    var step = (decimal)Math.Pow(10, precision); 
    var upperBoundry = (step - 1) * (decimal)Math.Pow(10, -scale); 
    var lowerBoundry = -1 * upperBoundry; 

    //Truncate decimal to scale 
    //If the truncated value does not equal the original, it must've been out of scale 
    step = (decimal)Math.Pow(10, scale); 
    var truncated = Math.Truncate(step * valueToCheck)/step; 

    return (valueToCheck <= upperBoundry) 
     && (valueToCheck >= lowerBoundry) 
     && truncated == valueToCheck; 
} 
+1

這確實是一個非常乾淨的方法來強制執行規模,但研究我的使用案例,舍入是可以接受的,但100%是一個偉大的觀點,什麼是更重要的,執行規模或接受圓值。在999.09的情況下,該值仍然在SQL中的TSQL Decimal(4,1​​)內,但會導致存儲四捨五入的值999.1。最後,根據我寫下我的問題的方式,而不是確切地說明這一點,我認爲你的答案仍然可以回答它,即使它不能完全解決我可能允許四捨五入的問題。 – TravisWhidden

+0

@TravisWhidden謝謝!至少,您可以去除截斷檢查,並且該函數將執行與您當前正在工作的方式相同的方式,而無需字符串操作。 – MrZander