حسین احمدی
بنیانگذار توسینسو و برنامه نویس و توسعه دهنده ارشد وب

استفاده از کلاس SqlBulkCopy به صورت Generic

چگونه رکوردهای زیادی در یک برنامه سی شارپی ایجاد کنیم ؟ در این مطلب با نحوه استفاده از کلاس SqlBulkCopy در سی شارپ برای درج انبوه رکورد ها در سی شارپ آشنا شدیم. در این مطلب با نحوه پیاده سازی یک متد Generic برای Bulk Insert آشنا می شویم. برای پیاده سازی این متد جنریک از قابلیت Reflection در سی شارپ استفاده می کنیم. ساختار جدول مورد استفاده از در این مطلب، همان جدولی است که در مطلب قبلی ایجاد کردیم. در ابتدا یک کلاس به عنوان مدل به صورت زیر تعریف می کنیم:

دوره های شبکه، برنامه نویسی، مجازی سازی، امنیت، نفوذ و ... با برترین های ایران
[Table("SampleTable")]
public class SampleTableModel
{
    [Column("From")]
    public string From { get; set; }
    [Column("To")]
    public string To { get; set; }
    public DateTime Date { get; set; }
    public decimal Balance { get; set; }
}

همانطور که مشاهده می کنید در این کلاس از بعضی Attribute ها مانند Table یا Column استفاده کردیم. استفاده از این Attribute ها برای مشخص کردن نام جدول مقصد که اطلاعات مدل داخل آن درج می شود و نام ستون های معادل هر خصوصیت است. در صورت عدم استفاده از این Attribute ها نام کلاس و نام خصوصیت ها به عنوان نام جدول و ستون معادل در نظر گرفته می شوند. ابتدا نحوه استفاده از متد BulkInsert را با در زیر ببینیم و بعد نحوه پیاده سازی آن را بررسی کنیم:

List<SampleTableModel> models = new List<SampleTableModel>();

for (int i = 0; i < 1000000; i++)
{
    models.Add(new SampleTableModel()
    {
        From = "Hossein Ahmadi",
        To = "Mohammad Nasiri",
        Date = DateTime.UtcNow,
        Balance = 10000
    });
}

BulkInsert(models);

کد بالا نیاز به توضیح زیاده ندارد، تعریف یک لیست از حدود 1 میلیون رکود بر اساس مدلی که ایجاد کردیم و فراخوانی متد BulkInsert برای درج رکوردها، شئ models به عنوان پارامتر ورودی به متد BulkInsert ارسال می شود. اما نحوه پیاده سازی متد BulkInsert رو در زیر مشاهده می کنید:

public static void BulkInsert<T>(List<T> items)
{
    var modelType = typeof(T);
    var properties = modelType.GetProperties();

    DataTable table = new DataTable();
    foreach (var property in properties)
    {
        table.Columns.Add(property.Name, property.PropertyType);
    }
    foreach (var item in items)
    {
        var row = table.NewRow();
        foreach (var property in properties)
        {
            row[property.Name] = property.GetValue(item);
        }
        table.Rows.Add(row);
    }
    using (var cnn = new SqlConnection("data source=.; initial catalog=SampleForBulkInsert; user id=sa; password=1"))
    {
        var bulkCopy = new SqlBulkCopy(cnn);
        foreach (var property in properties)
        {
            var propertyColumn = property.GetCustomAttribute<ColumnAttribute>()?.Name ?? property.Name;
            bulkCopy.ColumnMappings.Add(property.Name, propertyColumn);
        }

        var tableName = modelType.GetCustomAttribute<TableAttribute>()?.Name ?? modelType.Name;
        bulkCopy.DestinationTableName = tableName;
        cnn.Open();
        bulkCopy.WriteToServer(table);
    }
}

در کد بالا، ابتدا بوسیله Reflection نوع پارامتر T و لیست خصوصیت های آن را میگیریم:

var modelType = typeof(T);
var properties = modelType.GetProperties();

قدم بعدی ایجاد DataTable و تعریف ستون های داخل آن بر اساس خصوصیت های مدل است:

DataTable table = new DataTable();
foreach (var property in properties)
{
    table.Columns.Add(property.Name, property.PropertyType);
}

بعد از ایجاد ستون ها، باید بر اساس آیتم های داخل models سطرهای داخل table را ایجاد کنیم:

foreach (var item in items)
{
    var row = table.NewRow();
    foreach (var property in properties)
    {
        row[property.Name] = property.GetValue(item);
    }
    table.Rows.Add(row);
}

حالا آماده ایم که عملیات درج رو انجام بدیم، اما در ابتدا باید Mapping ها رو برای SqlBulkCopy تعریف کنیم که این کار رو بوسیله Reflection و گرفتن Attribute های Table و Column که بر روی کلاس مدل و خصوصیت ها قرار دادیم انجام میدیم:

var bulkCopy = new SqlBulkCopy(cnn);
foreach (var property in properties)
{
    var propertyColumn = property.GetCustomAttribute<ColumnAttribute>()?.Name ?? property.Name;
    bulkCopy.ColumnMappings.Add(property.Name, propertyColumn);
}

var tableName = modelType.GetCustomAttribute<TableAttribute>()?.Name ?? modelType.Name;
bulkCopy.DestinationTableName = tableName;

و در نهایت اجرای عملیات Bulk Insert:

cnn.Open();
bulkCopy.WriteToServer(table);

خاصیت متد BulkInsert این هست که به راحتی بر روی هر مدلی قابل انجام هست و نیازی به نوشتن کدهای مربوط به SqlBulkCopy و تعریف Mapping نیست و این کار به صورت خودکار در داخل متد BulkInsert انجام میشه.


حسین احمدی
حسین احمدی

بنیانگذار توسینسو و برنامه نویس و توسعه دهنده ارشد وب

حسین احمدی ، بنیانگذار TOSINSO ، توسعه دهنده وب و برنامه نویس ، بیش از 12 سال سابقه فعالیت حرفه ای در سطح کلان ، مشاور ، مدیر پروژه و مدرس نهادهای مالی و اعتباری ، تخصص در پلتفرم دات نت و زبان سی شارپ ، طراحی و توسعه وب ، امنیت نرم افزار ، تحلیل سیستم های اطلاعاتی و داده کاوی ...

نظرات