← Назад к вопросам

Приведи пример написанных драйверов

1.7 Middle🔥 82 комментариев
#Другое

Комментарии (2)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Примеры написанных драйверов для C# Backend

В контексте C# Backend разработки, драйверы обычно относятся к клиентским библиотекам или адаптерам для взаимодействия с внешними системами: базами данных, сервисами, протоколами или оборудованием. Я приведу примеры из разных категорий, демонстрируя архитектурные подходы и код.

1. Драйвер для базы данных (минималистичный ORM-подобный драйвер)

Этот драйвер абстрагирует работу с SQL Server, предоставляя простой API для CRUD операций.

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq.Expressions;

public interface IDatabaseDriver
{
    int ExecuteNonQuery(string sql, object parameters = null);
    IEnumerable<T> Query<T>(string sql, object parameters = null);
    T QuerySingle<T>(string sql, object parameters = null);
}

public class SqlServerDriver : IDatabaseDriver
{
    private readonly string _connectionString;

    public SqlServerDriver(string connectionString)
    {
        _connectionString = connectionString;
    }

    public int ExecuteNonQuery(string sql, object parameters = null)
    {
        using var connection = new SqlConnection(_connectionString);
        using var command = new SqlCommand(sql, connection);
        
        if (parameters != null)
            AddParameters(command, parameters);
        
        connection.Open();
        return command.ExecuteNonQuery();
    }

    public IEnumerable<T> Query<T>(string sql, object parameters = null)
    {
        using var connection = new SqlConnection(_connectionString);
        using var command = new SqlCommand(sql, connection);
        
        if (parameters != null)
            AddParameters(command, parameters);
        
        connection.Open();
        using var reader = command.ExecuteReader();
        
        var results = new List<T>();
        while (reader.Read())
        {
            results.Add(MapToObject<T>(reader));
        }
        return results;
    }

    private T MapToObject<T>(IDataRecord record)
    {
        var obj = Activator.CreateInstance<T>();
        var properties = typeof(T).GetProperties();
        
        foreach (var prop in properties)
        {
            if (record[prop.Name] != DBNull.Value)
                prop.SetValue(obj, record[prop.Name]);
        }
        return obj;
    }

    private void AddParameters(SqlCommand command, object parameters)
    {
        var properties = parameters.GetType().GetProperties();
        foreach (var prop in properties)
        {
            command.Parameters.AddWithValue($"@{prop.Name}", prop.GetValue(parameters));
        }
    }
}

2. Драйвер для внешнего REST API

Драйвер для взаимодействия с гипотетическим Weather API, реализующий повторные попытки и логирование.

using System;
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;

public class WeatherApiDriver
{
    private readonly HttpClient _httpClient;
    private readonly ILogger<WeatherApiDriver> _logger;
    private readonly string _apiKey;

    public WeatherApiDriver(HttpClient httpClient, ILogger<WeatherApiDriver> logger, string apiKey)
    {
        _httpClient = httpClient;
        _logger = logger;
        _apiKey = apiKey;
        _httpClient.BaseAddress = new Uri("https://api.weather.example.com/");
        _httpClient.DefaultRequestHeaders.Add("User-Agent", "WeatherDriver/1.0");
    }

    public async Task<WeatherData> GetWeatherAsync(string city, int maxRetries = 3)
    {
        var url = $"/v1/current?city={Uri.EscapeDataString(city)}&apikey={_apiKey}";
        
        for (int attempt = 1; attempt <= maxRetries; attempt++)
        {
            try
            {
                _logger.LogInformation($"Запрос погоды для {city}, попытка {attempt}");
                
                var response = await _httpClient.GetAsync(url);
                response.EnsureSuccessStatusCode();
                
                var json = await response.Content.ReadAsStringAsync();
                var weatherData = JsonSerializer.Deserialize<WeatherData>(json, new JsonSerializerOptions
                {
                    PropertyNameCaseInsensitive = true
                });
                
                return weatherData;
            }
            catch (HttpRequestException ex) when (attempt < maxRetries)
            {
                _logger.LogWarning($"Сбой при запросе к Weather API: {ex.Message}");
                await Task.Delay(1000 * attempt); // Экспоненциальная задержка
            }
        }
        
        throw new InvalidOperationException($"Не удалось получить данные о погоде для {city} после {maxRetries} попыток");
    }
}

public class WeatherData
{
    public string City { get; set; }
    public double Temperature { get; set; }
    public string Conditions { get; set; }
    public DateTime Updated { get; set; }
}

3. Драйвер для очереди сообщений (RabbitMQ)

Простой драйвер для публикации и потребления сообщений через RabbitMQ с поддержкой сериализации JSON.

using System;
using System.Text;
using System.Text.Json;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;

public class RabbitMqDriver : IDisposable
{
    private readonly IConnection _connection;
    private readonly IModel _channel;
    private readonly string _exchangeName;

    public RabbitMqDriver(string hostname, string exchangeName)
    {
        var factory = new ConnectionFactory() { HostName = hostname };
        _connection = factory.CreateConnection();
        _channel = _connection.CreateModel();
        _exchangeName = exchangeName;
        
        _channel.ExchangeDeclare(exchange: _exchangeName, type: ExchangeType.Direct);
    }

    public void Publish<T>(T message, string routingKey)
    {
        var json = JsonSerializer.Serialize(message);
        var body = Encoding.UTF8.GetBytes(json);
        
        var properties = _channel.CreateBasicProperties();
        properties.Persistent = true;
        properties.ContentType = "application/json";
        
        _channel.BasicPublish(
            exchange: _exchangeName,
            routingKey: routingKey,
            basicProperties: properties,
            body: body
        );
        
        Console.WriteLine($"Отправлено сообщение: {routingKey}");
    }

    public void Subscribe<T>(string queueName, string routingKey, Action<T> handler)
    {
        _channel.QueueDeclare(
            queue: queueName,
            durable: true,
            exclusive: false,
            autoDelete: false
        );
        
        _channel.QueueBind(
            queue: queueName,
            exchange: _exchangeName,
            routingKey: routingKey
        );
        
        var consumer = new EventingBasicConsumer(_channel);
        consumer.Received += (model, ea) =>
        {
            try
            {
                var body = ea.Body.ToArray();
                var json = Encoding.UTF8.GetString(body);
                var message = JsonSerializer.Deserialize<T>(json);
                
                handler(message);
                
                _channel.BasicAck(ea.DeliveryTag, false);
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Ошибка обработки сообщения: {ex.Message}");
                _channel.BasicNack(ea.DeliveryTag, false, true);
            }
        };
        
        _channel.BasicConsume(
            queue: queueName,
            autoAck: false,
            consumer: consumer
        );
    }

    public void Dispose()
    {
        _channel?.Close();
        _connection?.Close();
    }
}

Ключевые принципы написания драйверов

При создании драйверов для бэкенд-разработки на C# я руководствуюсь следующими принципами:

Абстракция и инкапсуляция

  • Драйвер скрывает сложность взаимодействия с внешней системой
  • Предоставляет простой, понятный API для основной логики приложения
  • Инкапсулирует протоколы, форматы данных и ошибки

Управление ресурсами

  • Правильная реализация IDisposable для ресурсов (соединения, потоки)
  • Использование using блоков и IAsyncDisposable
  • Пул соединений для баз данных и сетевых ресурсов

Обработка ошибок и устойчивость

  • Повторные попытки с экспоненциальной задержкой
  • Circuit Breaker паттерн для предотвращения каскадных сбоев
  • Детальное логирование для диагностики проблем
  • Грейсфул деградация функциональности

Производительность

  • Асинхронные методы (async/await) для неблокирующих операций
  • Кэширование частых запросов
  • Пакетная обработка операций
  • Оптимизация сериализации/десериализации

Конфигурируемость

  • Dependency Injection через конструктор
  • Опции конфигурации через IOptions pattern
  • Поддержка различных окружений (dev/stage/prod)

Паттерны проектирования в драйверах

В примерах выше использованы несколько ключевых паттернов:

  1. Adapter - преобразует интерфейс внешней системы к ожидаемому клиентскому интерфейсу
  2. Factory - создание сложных объектов (соединений, каналов)
  3. Strategy - различные алгоритмы сериализации, политики повторов
  4. Observer - обработка входящих сообщений в очереди

На практике, в enterprise-приложениях драйверы часто реализуют дополнительные функции: метрики (Prometheus, OpenTelemetry), трассировку распределенных транзакций, здоровье (Health Checks), конфигурацию через AppSettings и валидацию входных данных.

Код драйверов должен быть покрыт unit- и integration-тестами, с использованием моков для внешних зависимостей и тестовых контейнеров (Testcontainers) для реалистичного тестирования интеграции.

Приведи пример написанных драйверов | PrepBro