Windows Forms uygulamalarında Excel verilerini DataGridView’e aktarmak, genellikle Microsoft.Office.Interop.Excel kütüphanesi kullanılarak yapılır. Ancak bu yaklaşım; uygulamanın UI (Kullanıcı Arayüzü) thread’ini kilitler, arka planda kapanmayan “EXCEL.EXE” process’leri bırakır ve ciddi memory leak (bellek sızıntısı) sorunlarına yol açar.
Bu detaylı rehberde; ExcelDataReader kullanarak devasa veri setlerini asenkron (async/await) olarak nasıl okuyacağımızı, verileri nesnelere (DTO) nasıl haritalayacağımızı (mapping) ve DataGridView’in render darboğazlarını nasıl aşacağımızı adım adım inceleyeceğiz.
1. Mimari Karar: Neden DataTable Yerine Typed List (DTO)?
Excel verisini doğrudan DataTable olarak okuyup DataSource‘a basmak küçük dosyalar için çalışır. Ancak 100.000 satırlık verilerde DataTable ciddi bir RAM tüketimine neden olur. Bunun yerine veriyi kendi oluşturduğumuz sınıflara (Data Transfer Object) bind edeceğiz.
Örnek Modelimiz:
public class ProductExcelModel
{
public string ProductCode { get; set; }
public string ProductName { get; set; }
public decimal Price { get; set; }
public int StockQuantity { get; set; }
}2. Asenkron Excel Okuma Servisi (Non-Blocking UI)
Nuget üzerinden ExcelDataReader ve ExcelDataReader.DataSet paketlerini kurduktan sonra, okuma işlemini ana thread’den ayırıp Task içine alıyoruz. Böylece binlerce satır okunurken program donmaz, arkada bir loading animasyonu çalıştırabilirsiniz.
using ExcelDataReader;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
public class ExcelService
{
public async Task<List<ProductExcelModel>> ReadExcelAsync(string filePath)
{
// Dosya okuma işlemini arka plan thread'ine atıyoruz
return await Task.Run(() =>
{
var productList = new List<ProductExcelModel>();
// Türkçe karakter sorunları için Encoding kaydı (.NET Core / .NET 5+ için zorunlu)
System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);
using (var stream = File.Open(filePath, FileMode.Open, FileAccess.Read))
{
using (var reader = ExcelReaderFactory.CreateReader(stream))
{
// İlk satır başlık (Header) ise bir satır atla
reader.Read();
while (reader.Read())
{
// Hücre verilerini okuyup nesneye mapliyoruz (Null check'ler kritik)
var product = new ProductExcelModel
{
ProductCode = reader.GetValue(0)?.ToString() ?? string.Empty,
ProductName = reader.GetValue(1)?.ToString() ?? string.Empty,
// Sayısal değerler için güvenli dönüşüm (Safe Casting)
Price = decimal.TryParse(reader.GetValue(2)?.ToString(), out decimal parsedPrice) ? parsedPrice : 0,
StockQuantity = int.TryParse(reader.GetValue(3)?.ToString(), out int parsedStock) ? parsedStock : 0
};
productList.Add(product);
}
}
}
return productList;
});
}
}3. DataGridView Performans İnce Ayarları (Extension Method)
100 bin satırı listeye çevirdik. Bunu DataGridView’e basarken yaşanacak titremeyi (flickering) ve yavaş çizmeyi engellemek için DoubleBuffered özelliğini Reflection ile zorla açmamız gerekiyor.
Bunun için bir Extension Method yazıyoruz:
using System.Reflection;
using System.Windows.Forms;
public static class DataGridViewExtensions
{
public static void EnableDoubleBuffering(this DataGridView dgv)
{
typeof(DataGridView).InvokeMember(
"DoubleBuffered",
BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.SetProperty,
null,
dgv,
new object[] { true });
}
}4. Uygulama Katmanı (Form Entegrasyonu)
Geriye sadece formu tasarlamak ve yazdığımız asenkron mimariyi tetiklemek kalıyor. BindingList<T> kullanımı, grid üzerindeki ekleme/silme operasyonlarının UI’a anında yansımasını sağlar.
public partial class MainForm : Form
{
private ExcelService _excelService;
private BindingList<ProductExcelModel> _bindingData;
public MainForm()
{
InitializeComponent();
_excelService = new ExcelService();
// Form yüklenirken performans ayarlarını uygula
dataGridView1.EnableDoubleBuffering();
dataGridView1.RowHeadersVisible = false; // Sol taraftaki ok işaretli boş sütunu kapatır, render hızlanır
dataGridView1.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.None; // Performans için kritik
}
private async void btnLoadExcel_Click(object sender, EventArgs e)
{
using (OpenFileDialog ofd = new OpenFileDialog() { Filter = "Excel Dosyaları|*.xlsx;*.xls" })
{
if (ofd.ShowDialog() == DialogResult.OK)
{
try
{
btnLoadExcel.Enabled = false;
btnLoadExcel.Text = "Yükleniyor..."; // UI kitlenmediği için bu yazıyı kullanıcı anında görür
// Asenkron okumayı bekliyoruz
var data = await _excelService.ReadExcelAsync(ofd.FileName);
// Gelen Listeyi BindingList'e çevirip Grid'e basıyoruz
_bindingData = new BindingList<ProductExcelModel>(data);
dataGridView1.DataSource = _bindingData;
MessageBox.Show($"{data.Count} satır başarıyla yüklendi.", "Başarılı", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
catch (Exception ex)
{
MessageBox.Show($"Okuma sırasında hata: {ex.Message}");
}
finally
{
btnLoadExcel.Enabled = true;
btnLoadExcel.Text = "Excel Yükle";
}
}
}
}
}Özet
Bu mimari sayesinde;
- Uygulama okuma esnasında donmaz (Asynchronous Programming).
- Arka planda başıboş
EXCEL.EXEkalmaz (Garbage Collector dostu, Interop yok). - DataGridView kaydırılırken takılmaz (Double Buffering).
- Veri yapısı Typesafe olduğu için (DTO), ileride LINQ sorguları atmak saniyeler sürer.