십진 유형은 메모리 내 Sqlite 데이터베이스에서 double 또는 int로 읽습니다.

.net-core c# dapper sqlite

문제

닷넷의 Sqlite에 대한 경험이 너무 많지 않지만 내가 보는 행동은 다소 이상합니다. project.json 과 함께 .Net 핵심 응용 프로그램이 있다고 가정 해 보겠습니다.

{
  "version": "1.0.0-*",
  "buildOptions": {
    "debugType": "portable",
    "emitEntryPoint": true
  },
  "dependencies": {
    "Microsoft.Data.Sqlite": "1.0.0",
    "Dapper": "1.50.2"
  },
  "frameworks": {
    "netcoreapp1.0": {
      "dependencies": {
        "Microsoft.NETCore.App": {
          "type": "platform",
          "version": "1.0.0"
        }
      },
      "imports": "dnxcore50"
    }
  }
}

또한 우리는 간단한 클래스 Item :

public class Item
{
    public Item() { }

    public Item(int id, string name, decimal price)
    {
        this.Id = id;
        this.Name = name;
        this.Price = price;
    }

    public int Id { get; set; }
    public string Name { get; set; }
    public decimal Price { get; set; }
}

그런 다음 메모리 내 데이터베이스를 만들고 데이터로 채 웁니다 (Dapper 사용).

var connection = new SqliteConnection("Data Source=:memory:");
connection.Open();
connection.Execute("CREATE TABLE IF NOT EXISTS Items(Id INT, Name NVARCHAR(50), Price DECIMAL)");

var items = new List<Item>
{
    new Item(1, "Apple", 3m),
    new Item(2, "Banana", 1.4m)
};
connection.Execute("INSERT INTO Items(Id, Name, Price) VALUES (@Id, @Name, @Price)", items);

그런 다음 Items 테이블에서 읽으려고합니다.

var dbItems = connection.Query<Item>("SELECT Id, Name, Price FROM Items").ToList();

솔루션을 실행할 때 다음 예외가 발생합니다.

처리되지 않은 예외 : System.InvalidOperationException : 열 2 (Price = 1.4 - Double) 구문 분석 오류 ---> System.Invali dCastException : 'System.Double'형식의 개체를 'System.Int64'형식으로 캐스팅 할 수 없습니다.

좋아, 그렇다면 Microsoft.Data.Sqlite 를 사용하여 데이터를 가져 오려고했습니다.

var command = connection.CreateCommand();
command.CommandText = "SELECT Price FROM Items";
var reader = command.ExecuteReader();
while (reader.Read())
{
    Console.WriteLine(reader[0].GetType());
}

결과적으로 나는 얻는다 :

System.Int64 // Price = 3                                                                                                         
System.Double // Price = 1.4

십진법 가격으로 실제 데이터베이스에서 쿼리를 실행 해 보았습니다. 반환 된 데이터 형식은 정확하며 항상 예상대로 10 진수입니다.
어떤 방향으로 나아가야합니까? 메모리 데이터베이스에 문제가 있습니까? 10 진수와 일관되게 만드는 방법은 무엇입니까?

수락 된 답변

이것은 SQLite의 기능입니다. https://sqlite.org/faq.html#q3 링크를 참조하십시오.


인기 답변

SQLite에서 Numeric / Decimal Column 데이터 유형을 가진 Table을 생성하면 Numeric type 연관 관계에 연관되며 기본 동작은 소수에 값이 없으면 정수를 저장하거나 소수에 소수가 포함 된 경우 실수를 저장하는 것입니다. 이것은 가장 많이 사용되는 정수 값 (중소 규모 어플리케이션의 경우 백만 값 미만)이 실제 데이터 유형보다 적은 바이트를 필요로하므로 바이트 저장 공간을 절약하는 데 도움이됩니다.

정수 (약) :

  • Â ± 128> 1 바이트 (2 ^ 8-1)
  • Â ± 32,768> 2 바이트 (2 ^ 16-1)
  • ± 8,388,608> 3 바이트 (2 ^ 24-1)
  • Â ± 2,147,483,648> 4 바이트 (2 ^ 32-1)
  • ± 140,737,488,355,328> 6 바이트 (2 ^ 48-1)
  • ± 9,223,372,036,854,780,000> 8 바이트 (2 ^ 64-1)

8 바이트 실제 IEEE 데이터 형식 반대

실수를 항상 저장해야하는 경우 실수 유형으로 간주하기 위해 float / real 데이터 유형을 선언해야합니다. 따라서 정수를 SQLite에 보내더라도 실수로 저장됩니다

출처 : https://sqlite.org/datatype3.html

나는 실수가있을 수 있음을 알고 있지만, C # Decimal 데이터 유형과 SQLite를 wal 모드의 디스크에 데이터베이스로 사용하여 테스트를 수행했습니다. C #으로 모든 연산이 수행되면 반올림 오류가 발생하지 않았습니다. 하지만 SQLite에서 직접 계산을했을 때 2 개의 정수 나누기 때문에 반올림 오류와 잘못된 계산이있었습니다.

EF6 및 Dapper를 사용하여 숫자 값 (정수 / SQLite에서의 실수)을 읽고 쓰고 오류가 발생하지 않습니다.

이제는 SQLite 숫자 동작을 활용하여 디스크 공간을 절약하고 TypeHandler를 구현할 계획이므로 SQL Server Money 구현을 복제 할 수 있습니다 (내부적으로 SQL Server는 8 바이트 정수를 저장하고 메모리에로드 할 때 10000으로 나눕니다) :

public class NumericTypeHandler : SqlMapper.TypeHandler<decimal>
{
    public override void SetValue(IDbDataParameter parameter, decimal value)
    {
        parameter.Value = (long)(value / 10000);
    }

    public override decimal Parse(object value)
    {
        return ((decimal)value)/10000M;
    }
}

또한 디스크 공간을 절약하고 성능을 향상시키기 위해 Datetime UnixEpoch 구현을 설정했습니다.

노트 :

SQLite는 낮은 수요 asp.net 응용 프로그램에서 수십 명의 사용자를 처리 할 수 ​​있습니다. 트릭은 pragma 지시문 및 기타 사항이있는 튠업 SQLite입니다.

  • WAL 모드 : 더 나은 동시성과 성능을 관리하는 데 도움이됩니다.
  • 페이지 크기 : if가 파일 시스템의 클러스터 크기와 일치하면 성능이 향상됩니다.
  • 공유 캐시 모드 : 여러 스레드에서 액세스 할 때 데이터베이스 잠금 대신 테이블 잠금을 구현합니다.
  • Datetime UnixEpoch : 텍스트 ISO datetime ( "2012-12-21T17 : 30 : 24"= 20 바이트) 대신 초 (4 바이트)를 절약합니다.
  • 캐시 크기 : 마지막으로 액세스 한 페이지를 메모리에 유지하는 저장된 I / O 액세스

또한 C # 측에서 몇 가지 개선 사항을 만들었습니다.

  • ToString 메서드를 재정의하는 정수로 데이터를 저장하고 생성자 오버로드로 DateTime으로 메모리에로드하기 위해 DateTime 값에 대한 사용자 지정 DateTime 구조체 를 만들었습니다.
  • C #에서 사용자 지정 Numeric 구조체 (DateTime 1과 같은)를 작성하여 ToString 메서드를 재정의하는 센트로 비용을 절약하고 생성자 오버로드가있는 Decimal로 메모리에로드합니다.

불필요한 데이터 저장 방지 (오류 로그, 이메일 html 텍스트)

SQLite를 백엔드로 사용할 계획이라면 EFx 나 Dapper와 같은 ORM을 사용해야한다고 생각합니다. dapper를 사용하면 개발 시간 (기본 CRUD 기능 및 Linq 쿼리 변환)을 절약하기 위해 자체 프레임 워크 를 직접 작성해야합니다.

내가 찾은 최고의 도구는 https://github.com/linq2db/linq2db 이며, linq를 사용하여 매우 빠른 속도와 용이성을 갖춘 데이터베이스에 액세스 할 수 있습니다. CRUD 기능을 구현하는 것도 가능합니다.

이 대답이 도움이된다면 행복 할거야.

문안 인사



아래 라이선스: CC-BY-SA with attribution
와 제휴하지 않음 Stack Overflow
아래 라이선스: CC-BY-SA with attribution
와 제휴하지 않음 Stack Overflow