Соответствие типов данных C# и SQL Server: полное руководство
Всем привет! Я начинающий C# разработчик, пишу небольшое приложение для работы с базой данных SQL Server через Entity Framework. Столкнулся с проблемой: я не могу точно понять, какие типы данных в C# соответствуют типам в SQL Server.
Например, у меня есть таблица с колонками:
CREATE TABLE Users (
Id INT PRIMARY KEY,
Name NVARCHAR(100),
Salary DECIMAL(18,2),
IsActive BIT,
CreatedAt DATETIME2
);
В C# я создаю модель:
public class User
{
public int Id { get; set; } // INT -> int, тут вроде понятно
public string Name { get; set; } // NVARCHAR -> string, тоже ясно
public decimal Salary { get; set; } // DECIMAL -> decimal?
public bool IsActive { get; set; } // BIT -> bool?
public DateTime CreatedAt { get; set; } // DATETIME2 -> DateTime?
}
Но есть много других типов: BIGINT, SMALLINT, TINYINT, FLOAT, REAL, MONEY, UNIQUEIDENTIFIER, VARBINARY и т.д. Я постоянно путаюсь, особенно с числовыми типами. Например, какая разница между FLOAT и REAL в SQL и как их правильно маппить на C#? И что делать с типами, которых нет в C# напрямую, например, SQL_VARIANT или GEOGRAPHY?
Может кто-то составить полную таблицу соответствия? Буду очень благодарен за подробное объяснение с примерами!
<<Полное соответствие типов данных SQL Server и C# (.NET)
При работе с SQL Server из C# (через ADO.NET, Entity Framework, Dapper или другие ORM) важно понимать точное соответствие типов. Вот исчерпывающая таблица и пояснения:
Основные типы данных (наиболее часто используемые)
| SQL Server | C# (.NET) | Примечания |
|---|---|---|
| INT | int (Int32) | 4 байта, диапазон ±2.1 млрд |
| BIGINT | long (Int64) | 8 байт, очень большие числа |
| SMALLINT | short (Int16) | 2 байта, ±32,767 |
| TINYINT | byte | 1 байт, 0-255 (беззнаковый!) |
| BIT | bool | true/false или null |
| DECIMAL(p,s) | decimal | Точные десятичные числа |
| NUMERIC(p,s) | decimal | Синоним DECIMAL |
| MONEY | decimal | Специальный тип, но лучше маппить на decimal |
| SMALLMONEY | decimal | Аналогично |
| FLOAT(n) | double | Приблизительное число с плавающей точкой (8 байт) |
| REAL | float | Приблизительное число (4 байта) |
| NVARCHAR(n)/NVARCHAR(MAX) | string | Юникод-строка |
| VARCHAR(n)/VARCHAR(MAX) | string | Неюникод, но в C# всё равно string |
| CHAR(n)/NCHAR(n) | string | Фиксированная длина |
| DATETIME | DateTime | Точность до 3.33 мс |
| DATETIME2(p) | DateTime | Более высокая точность (до 100 нс) |
| SMALLDATETIME | DateTime | Точность до 1 минуты |
| DATE | DateTime (или DateOnly в .NET 6+) | Только дата |
| TIME(p) | TimeSpan (или TimeOnly в .NET 6+) | Только время |
| DATETIMEOFFSET(p) | DateTimeOffset | С часовым поясом |
| UNIQUEIDENTIFIER | Guid | GUID |
| VARBINARY(n)/VARBINARY(MAX) | byte[] | Массив байт |
| BINARY(n) | byte[] | Фиксированная длина |
| XML | string (или XDocument/XElement) | XML-данные |
| SQL_VARIANT | object | Может содержать любой тип |
Важные нюансы и примеры
1. Разница между FLOAT и REAL:
// SQL: FLOAT (53) -> C# double (8 байт)
double sqlFloat = 3.141592653589793;
// SQL: REAL (24) -> C# float (4 байта)
float sqlReal = 3.14159f;
// Внимание: точность разная!
double d = 0.1 + 0.2; // 0.30000000000000004
float f = 0.1f + 0.2f; // 0.3 (но это округление)
2. Работа с nullable типами:
// Если в SQL колонка допускает NULL, используйте nullable типы
public class Product
{
public int Id { get; set; } // NOT NULL
public string? Description { get; set; } // NVARCHAR NULL
public decimal? Price { get; set; } // DECIMAL NULL
public DateTime? DeletedAt { get; set; } // DATETIME2 NULL
}
3. Специальные типы (GEOGRAPHY, HIERARCHYID и др.):
// Для GEOGRAPHY используйте Microsoft.SqlServer.Types
// Установите NuGet пакет: Microsoft.SqlServer.Types
using Microsoft.SqlServer.Types;
public class Location
{
public SqlGeography GeoPoint { get; set; } // Для хранения координат
}
// Или используйте сторонние библиотеки (NetTopologySuite для EF Core)
4. Рекомендации по выбору типов:
- Для целых чисел: используйте наименьший подходящий тип (byte, short, int, long)
- Для денег: всегда decimal, никогда не float/double (из-за погрешностей)
- Для дат: DateTime для большинства случаев, DateTimeOffset если важен часовой пояс
- Для больших строк: используйте NVARCHAR(MAX) с типом string (но будьте осторожны с производительностью)
5. Пример полной модели с правильными типами:
public class Employee
{
public int Id { get; set; } // INT
public string FullName { get; set; } // NVARCHAR(200)
public byte Age { get; set; } // TINYINT (0-255)
public decimal Salary { get; set; } // DECIMAL(18,2)
public double Height { get; set; } // FLOAT
public bool IsActive { get; set; } // BIT
public DateTime BirthDate { get; set; } // DATE
public TimeSpan ShiftStart { get; set; } // TIME
public Guid Identifier { get; set; } // UNIQUEIDENTIFIER
public byte[] Avatar { get; set; } // VARBINARY(MAX)
public string? Notes { get; set; } // NVARCHAR(MAX) NULL
}
Запомните главное правило: всегда используйте типы C#, которые точно соответствуют типам SQL по диапазону и точности. При сомнениях проверяйте документацию Microsoft или используйте профилировщик для отслеживания реальных преобразований.
<<Как избежать ошибок при маппинге: практические советы
К таблице соответствия из первого ответа добавлю несколько важных моментов, которые помогут избежать типичных ошибок:
1. Используйте явное приведение типов при ручном маппинге
// Чтение из DataReader
using (var cmd = new SqlCommand("SELECT * FROM Users", connection))
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
var user = new User
{
Id = reader.GetInt32(0), // INT
Name = reader.GetString(1), // NVARCHAR
Salary = reader.GetDecimal(2), // DECIMAL
IsActive = reader.GetBoolean(3), // BIT
CreatedAt = reader.GetDateTime(4) // DATETIME2
};
}
}
2. Будьте осторожны с типами, которых нет в C#
Ответов пока нет.
Войдите чтобы оставить ответ
Блоги