نحوه استفاده از Dapper در پروژه های دات نت
هنگامی که با دات نت کار می کنید، تعامل با پایگاه های داده – به ویژه پایگاه های داده SQL – اجتناب ناپذیر است. رویکردهای رایج شامل ابزارهای ORM (نگاشت رابطه ای شی) مانند Entity Framework (EF Core) یا Dapper است.
Dapper در سرعت و کنترل برای عملیات CRUD عالی است. این مقاله مناسب بودن Dapper را برای پروژههای داتنت حیاتی عملکرد با روابط پایگاه داده سادهتر، با استفاده از پرسوجوهای SQL خام تحلیل میکند.
با توجه به اتکای Dapper به SQL خام، این امر همچنین آن را برای یکپارچه سازی با پایگاه داده های موجود عالی می کند (برخلاف بزرگترین رقیب آن EF Core که عمدتاً روی رویکرد کد اول کار می کند).
Dapper به عنوان یک ORM سبک وزن و با کارایی بالا با مزایای متعدد برجسته می شود. این مقاله شما را در ساخت یک API وب دات نت سبک با استفاده از نقاط پایانی پایه Dapper برای SQLite راهنمایی می کند، ابزاری ایده آل برای برنامه های توسعه محلی یا POC (اثبات مفهوم).
پیش نیازها
آخرین .NET SDK و زمان اجرا (در زمان نوشتن این مقاله NET 8 بود). اگر قبلاً این را نصب نکردهاید، میتوانید نصبکنندهها را اینجا بیابید.
ویرایشگر کد ترجیحی شما یا IDE (محیط توسعه یکپارچه) - برای مثال Visual Studio / VS Code / JetBrains Rider.
آشنایی با زبان برنامه نویسی C# و Dependency Injection
خط فرمان ویندوز / ترمینال سیستم عامل مک
در این آموزش موارد زیر را خواهید آموخت:
داپر چیست؟
نحوه راه اندازی یک Api وب دات نت با پشتیبانی پایگاه داده SQLite
استفاده از Dapper برای اقدامات اولیه CRUD و ادغام اولیه در یک پروژه Net Web Api با استفاده از الگوی مخزن
Dapper را برای خواندن و نوشتن از/به پایگاه داده Sqlite نصب و پیکربندی کنید
توجه: مطالب آموخته شده در این آموزش را می توان برای هر برنامه .Net که نیاز به تعامل با پایگاه داده دارد، اعمال کرد.
فهرست مطالب:
نحوه اضافه کردن وابستگی های پروژه
نحوه اجرای برنامه برای اولین بار
نحوه ایجاد پایگاه داده SQLite و ایجاد جدول مشتریان
CRUD - ایجاد، خواندن، به روز رسانی، حذف
نحوه اتصال مخزن به نقاط پایانی API Web
داپر چیست؟
Dapper یک "micro ORM" است که دسترسی داده های سبک وزن و با کارایی بالا را با حداقل انتزاع فراهم می کند. به پرس و جوهای SQL متکی است (اینها پرس و جوهایی هستند که هنگام تعامل با پایگاه داده SQL استفاده می کنید) به عنوان مثال:
SELECT * FROM CUSTOMERS WHERE ID = 1
، نگاشت نتایج به طور مستقیم به اشیا.
در اینجا نحوه توصیف Dapper's README این است که چه کاری انجام می دهد:
این [Dapper] یک API ساده و کارآمد برای فراخوانی SQL با پشتیبانی از دسترسی همزمان و ناهمزمان به دادهها فراهم میکند و امکان درخواستهای بافر و غیر بافر را فراهم میکند. – مخزن Dapper github README
بیایید به یک مثال نگاه کنیم تا ببینیم چگونه کار می کند.
بگویید ما یک شی مانند این داریم:
public class Customer { public int ID {get;set;} public string FirstName {get;set;} public string LastName {get;set} public string Email {get;set;} public DateTime DOB {get;set;} public Customer(){ } }
یک ORM (نگاشت شی - رابطه ای) مانند یک مترجم بین یک زبان برنامه نویسی و یک پایگاه داده است که به نرم افزار اجازه می دهد تا با استفاده از اشیاء برنامه نویسی آشنا به جای پرس و جوهای مستقیم SQL با پایگاه های داده تعامل داشته باشد و مدیریت و دستکاری داده ها را در برنامه ها آسان تر می کند.
از آنجایی که Dapper یک "Micro-ORM" است، در حد وسط بین SQL مستقیم و یک ORM کامل مانند Entity Framework (EF) عمل می کند. بسیاری از آپشن های اولیه را دارد، اما با تمام "نفخ" همراه نیست و آن را به یک ابزار پایگاه داده ایمن و سریعتر تبدیل میکند.
مزایای داپر
کارایی:
Dapper به دلیل عملکرد سبک و سریع خود شناخته شده است، به خصوص زمانی که با مجموعه داده های بزرگ یا عملیات خواندن سنگین سروکار دارید. در مقایسه با Entity Framework دارای ویژگی های بیشتر، سربار کمتری دارد.
کنترل بر روی پرس و جوهای SQL:
Dapper به شما این امکان را می دهد که پرس و جوهای SQL خود را به صراحت بنویسید و کنترل کنید. هنگامی که به کنترل دقیق بر روی SQL تولید شده نیاز دارید یا هنگام برخورد با پرس و جوهای پیچیده، می تواند مفید باشد.
نقطه ضعف این است که شما باید روش های مدیریت و ساختار خود را در پایگاه کد خود پیدا کنید.
انعطاف پذیری نقشه برداری:
Dapper انعطاف پذیری بیشتری را در نگاشت نتایج پایگاه داده به اشیا فراهم می کند. این قرارداد به اندازه Entity Framework تحمیل نمی کند و به شما امکان می دهد نتایج پرس و جو را به راحتی به ساختارهای شی سفارشی نگاشت کنید.
مثال: فرض کنید می خواهید شیء SQL را فوراً به یک ViewModel با اعمال منطق سفارشی به جای شیء DTO، نگاشت کنید.
حداقل انتزاع:
Dapper یک میکرو ORM با حداقل انتزاع است. اگر ترجیح میدهید نزدیکتر به فلز کار کنید و میخواهید کنترل بیشتری روی SQL و کد دسترسی به دادهها داشته باشید، Dapper ممکن است مناسبتر باشد.
مناسب برای سناریوهای Read-Heavy:
در سناریوهایی که عملکرد خواندن بسیار مهم است، و شما می خواهید پرس و جوها را برای موارد استفاده خاص بهینه کنید، سادگی و کنترل مستقیم Dapper می تواند سودمند باشد.
چالش های داپر
نوشتن دستی پرس و جو SQL:
Dapper از شما می خواهد که پرس و جوهای SQL خام بنویسید. در حالی که این انعطافپذیری را فراهم میکند، همچنین به این معنی است که باید ساخت و پارامترسازی پرس و جو را به صورت دستی انجام دهید، که به طور بالقوه شما را در معرض خطرات تزریق SQL قرار میدهد (در ادامه در مورد آن صحبت خواهیم کرد) تا با استفاده از Dapper از این کار جلوگیری کنید.
انتزاعات سطح بالا محدود:
Dapper یک API سطح پایین تری در مقایسه با ORM های کامل مانند Entity Framework ارائه می دهد. این بدان معنی است که شما باید کد بیشتری را برای عملیات معمول CRUD بنویسید و فاقد انتزاعات سطح بالا مانند ردیابی خودکار تغییرات و مهاجرت هستید.
بدون پشتیبانی داخلی LINQ
کسانی که با LINQ آشنا هستند می دانند که چقدر می تواند در هنگام پرس و جو از مجموعه داده ها و مخازن داده مفید باشد.
Dapper پشتیبانی داخلی LINQ را ارائه نمی دهد. در حالی که LINQ را می توان همراه با Dapper مورد استفاده قرار داد، اما سطح یکپارچگی و بیانی مشابه یک ORM مانند Entity Framework را ندارد.
کنوانسیون های محدود:
Dapper به اندازه برخی ORM های دیگر دارای قراردادها و پیش فرض ها نیست. این بدان معنی است که شما ممکن است نیاز به ارائه نگاشت ها و پیکربندی های واضح تری در سناریوهای خاص داشته باشید (اما همانطور که قبلاً گفته شد، این بسته به نیاز شما می تواند هم یک نزول و هم یک مزیت باشد).
ویژگی های کمتر برای مدل های داده پیچیده:
Dapper ممکن است برای برنامههایی با مدلهای داده پیچیده که شامل روابط پیچیده بین موجودیتها هستند، انتخاب ایدهآلی نباشد، زیرا مدیریت چنین روابطی ممکن است به تلاش دستی بیشتری نیاز داشته باشد.
بدون بارگذاری تنبل:
Dapper از بارگیری تنبل خارج از جعبه پشتیبانی نمی کند. اگر به بارگذاری تنبل برای موجودیت های مرتبط نیاز دارید، ممکن است لازم باشد خودتان آن را پیاده سازی کنید یا سایر ORM ها مانند Entity Framework را که این ویژگی را ارائه می دهند در نظر بگیرید.
شروع شدن
من قصد دارم در این آموزش به شما یاد بدهم که چگونه پروژه و تمام وابستگی های مورد نیاز را با استفاده از CLI به جای ویرایشگر کد / IDE راه اندازی کنید. من این کار را انجام می دهم زیرا:
خواهید دید که انجام این نوع کارها در ترمینال چقدر آسان است
با استفاده از CLI در مقابل تکیه بر منوها و ابزارهای زمینه، به ایجاد اعتماد به نفس شما کمک می کند
این به شما نشان می دهد که استفاده از ترمینال چقدر سریعتر از استفاده از رابط کاربری گرافیکی (رابط کاربری گرافیکی) است.
پس ویرایشگر خود را به دایرکتوری ریشه پروژه های شخصی خود باز کنید.
اکنون ترمینال را باز کرده و به آن پوشه در ترمینال خود بروید. من تمایل دارم پوشه پروژه هایم را برای دسترسی آسان نزدیک به ریشه خود نگه دارم. به عنوان مثال من دارم:
اگر قبلاً نمی دانستید، می توانید با استفاده از دستور cd
(تغییر دایرکتوری) به پوشه ها بروید.
پس از ورود به پوشه پروژه های خود، دستور زیر را بنویسید:
dotnet new webapi -n FCC_Dapper
پس این چه می کند؟
یک پوشه جدید به نام FCC_Dapper در فهرست پروژه های شما ایجاد می کند
یک پروژه WebApi .Net 8 جدید به نام FCC_Dapper ایجاد می کند
با استفاده از دستور cd FCC_Dapper
به پوشه پروژه بروید.
نکته: هنگام استفاده از ترمینال، به سادگی cd F
تایپ کرده و Tab را فشار دهید. این به طور خودکار تا آنجا که می تواند مسیر فایل را تکمیل می کند (تا زمانی که چندین مورد منطبق را پیدا کند). اگر فقط 1 مورد منطبق پیدا شود، تکمیل خودکار مسیر کامل را به پایان میرساند و cd FCC_Dapper
را برای شما باقی میگذارد. اگر چند مورد پیدا شد، دوباره Tab را فشار دهید و چندین گزینه را به شما نشان می دهد.
نحوه اضافه کردن وابستگی های پروژه
دستور زیر را اجرا کنید تا تمام وابستگی های مورد نیاز را اضافه کنید:
نکته: با دستورات ترمینال، میتوانید آنها را در یک دستور ترمینال بزرگ با استفاده از ;
یا کاراکترهای &&
.
در اینجا یک مثال است:
dotnet add package Dapper; dotnet add package Microsoft.Data.SQLite
یا:
dotnet add package Dapper && dotnet add package Microsoft.Data.SQLite
کاراکتر &&
مانند یک چک بولی عمل می کند، به این صورت که فرمان دوم فقط در صورتی اجرا می شود که اولی خراب نشود/خطا کند.
اکنون باید فایل ها و پوشه های زیر را در ویرایشگر کد / IDE خود مشاهده کنید:
نحوه حذف کد دیگ بخار
فایل Program.cs را باز کنید و تمام کدهای boilerplate را که از قبل پر شده است حذف کنید. می توانید با خیال راحت کد زیر را از Program.cs حذف کنید:
var summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" }; app.MapGet("/weatherforecast", () => { var forecast = Enumerable.Range(1, 5).Select(index => new WeatherForecast ( DateOnly.FromDateTime(DateTime.Now.AddDays(index)), Random.Shared.Next(-20, 55), summaries[Random.Shared.Next(summaries.Length)] )) .ToArray(); return forecast; }) .WithName("GetWeatherForecast") .WithOpenApi();
نحوه اجرای برنامه برای اولین بار
حالا اجرا کنید:
dotnet dev-certs https --trust
این امر از شما خواسته میشود گواهیهای توسعه را بپذیرید و به شما امکان میدهد وب api را با استفاده از پروتکل https
اجرا کنید. پس اگر اکنون اجرا می کنید:
dotnetrun --launch-profile https
شما باید موارد زیر را در کنسول خود مشاهده کنید:
... info: Microsoft.Hosting.Lifetime[14] Now listening on: https://localhost:{port} ...
در ابتدا صفحه 404 مانند زیر را مشاهده خواهید کرد:
با این حال، اگر /swagger
به URL اضافه کنید، اکنون رابط کاربری Swagger را خواهید دید که به ما اطلاع می دهد که هیچ نقطه پایانی تعریف نشده است (که در این مرحله قابل انتظار است)
Swagger مجموعه ای از ابزارها است که به توسعه دهندگان اجازه می دهد تا نقاط پایانی API خود را طراحی، مشاهده، آزمایش و مستند کنند. Swagger UI عنصر رابط کاربری مجموعه ابزار است که به شما امکان می دهد از طریق یک رابط وب با یکدیگر تعامل داشته باشید و نقاط پایانی خود را مشاهده کنید.
OpenAPI نام رسمی مشخصات است. توسعه مشخصات توسط ابتکار OpenAPI، که شامل 30 سازمان از مناطق مختلف دنیای فناوری - از جمله مایکروسافت، گوگل، IBM، و CapitalOne میشود، تشویق میشود. نرمافزار Smartbear، که شرکتی است که توسعه ابزارهای Swagger را رهبری میکند، همچنین یکی از اعضای OpenAPI Initiative است که به پیشروی تکامل مشخصات - Swagger، Smartbear کمک میکند.
نحوه ایجاد پایگاه داده SQLite و ایجاد جدول مشتریان
ایجاد فایل پایگاه داده SQLite.
دو راه برای انجام این کار وجود دارد:
گزینه 1: در خط فرمان / ترمینال sqlite3 customers.db
را اجرا کنید
گزینه 2: در ویرایشگر کد / IDE خود یک فایل به نام customers.db
ایجاد کنید
اگر گزینه 1 را انتخاب کنید، خروجی را در ترمینال خود مشاهده خواهید کرد، مانند زیر:
SQLite version 3.32.2 2021-07-12 15:00:17 Enter ".help" for usage hints.
دستور .database
را در خط فرمان / ترمینال خود اجرا کنید و نام و فایل های پایگاه داده پیوست شده را فهرست می کند.
اتصال Web API به پایگاه داده
با باز کردن فایل، یک رشته اتصال پیشفرض به فایل appsettings.json
اضافه کنید. کد زیر را به بالای فایل، در اولین {
. پس فایل appsettings.json
شما اکنون باید به شکل زیر باشد:
{ "ConnectionStrings": { "DefaultConnection": "Data Source=customers.db" }, "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } }, "AllowedHosts": "*" }
نحوه ایجاد جدول مشتری و در ابتدا دانه بندی داده ها
مرحله 1: یک پوشه پایگاه داده و یک فایل DatabaseUtilities.cs ایجاد کنید.
دو راه اصلی برای انجام این کار وجود دارد:
گزینه 1: یک پوشه به نام "Database" و در داخل پوشه یک فایل به نام DatabaseUtilities.cs
ایجاد کنید.
گزینه 2: از CLI استفاده کنید
خط فرمان ویندوز:
mkdir Database && cd Database && echo. > DatabaseUtilities.cs
ترمینال سیستم عامل مک:
mkdir Database && cd Database && touch DatabaseUtilities.cs
مرحله 2: کد زیر را به فایل DatabaseUtilities.cs اضافه کنید:
using Dapper; using Microsoft.Data.Sqlite; public static class DBUtilities { public static async Task<bool> InitializeDBAsync(this IWebApplication app) { var connectionString = app.Configuration.GetConnectionString("DefaultConnection"); var createSQL = @"CREATE TABLE IF NOT EXISTS Customer ( ID INTEGER PRIMARY KEY AUTOINCREMENT, FirstName TEXT, LastName TEXT, DOB DATE, Email TEXT );"; var insertSQL = @" INSERT INTO Customer (FirstName, LastName, DOB, Email) VALUES ('Tony', 'Stark', '1970-05-29', 'tony.stark@example.com'), ('Bruce', 'Wayne', '1972-11-11', 'bruce.wayne@example.com'), ('Peter', 'Parker', '1995-08-10', 'peter.parker@example.com'), ('Diana', 'Prince', '1985-04-02', 'diana.prince@example.com'), ('Clark', 'Kent', '1980-07-18', 'clark.kent@example.com'), ('Natasha', 'Romanoff', '1983-06-25', 'natasha.romanoff@example.com'), ('Wade', 'Wilson', '1977-02-19', 'wade.wilson@example.com'), ('Hal', 'Jordan', '1988-09-05', 'hal.jordan@example.com'), ('Steve', 'Rogers', '1920-07-04', 'steve.rogers@example.com'), ('Selina', 'Kyle', '1982-12-08', 'selina.kyle@example.com');"; using var connection = new SqliteConnection(connectionString); connection.Open(); using var transaction = connection.BeginTransaction(); try { await connection.ExecuteAsync(createSQL, transaction: transaction); // Check if the Customer table exists var tableExists = await connection.QueryFirstOrDefaultAsync<int>( "SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='Customer';", transaction: transaction); if (tableExists > 0) { // Table exists and populated, no need to seed database again return true; } await connection.ExecuteAsync(insertSQL, transaction: transaction); // Commit the transaction if everything is successful transaction.Commit(); connection.Close(); return true; } catch (Exception ex) { Console.WriteLine(ex.Message); // An error occurred, rollback the transaction transaction.Rollback(); connection.Close(); return false; } } }
بیایید این کد را ذره ذره تجزیه کنیم:
ابتدا یک متد افزونه در کلاس WebApplication
ایجاد می کند
سپس SQL مورد نیاز برای ایجاد یک پایگاه داده را ایجاد می کند
سپس یک اتصال به پایگاه داده SQLite با استفاده از نوع SqliteConnection
با عبور یک رشته اتصال ایجاد می کنیم و به برنامه اطلاع می دهیم که از فایل customers.db
به عنوان منبع داده استفاده کند.
در نهایت، دستورات SQL را به صورت ناهمزمان اجرا میکند، که در یک بلوک try/catch
با استفاده از روشهای توسعه قدرتمند Dapper روی شی connection
پیچیده شده است.
تراکنش های SQL
این کد از BeginTransaction
API در بسته SQLite استفاده می کند. سینتکس تراکنش روشی بسیار ایمنتر برای مدیریت دستورات SQL است که میخواهید چندین فرمان یا دستورالعملهای بزرگ را اجرا کنید.
دوباره، مانند قبل، از دستور using
می کنیم و یک اتصال ایجاد می کنیم - اما این بار یک دستور اضافی (تودرتو) using
دستور اضافه می کنیم و یک شی transaction
ایجاد می کنیم.
در اینجا میتوانید اطلاعات بیشتری درباره تراکنشهای SQL بخوانید، اما در اصل میتواند از اجرای دستورات بعدی در صورت خطا/یا اجرا نشدن دستور قبلی جلوگیری کند. در سناریویی که فرمان دوم به فرمان قبلی که با موفقیت اجرا می شود، وابستگی دارد عالی است.
اجرای دستورات و rollbacks
حالا بیایید درباره Rollbacks صحبت کنیم. بازگشتها فقط چیزی نیست که در سوپرمارکت پیدا کنید، بلکه یکی از آپشن های عالی در تراکنشهای SQL است.
در SQL، به یک ROLLBACK
مانند یک دکمه لغو فکر کنید. پس ، اگر در وسط انجام وظایف / دستورات در پایگاه داده هستید (مانند گفت ن، بهروزرسانی یا حذف چیزها)، وای! مشکلی پیش میآید یا نظرتان تغییر میکند، میتوانید ROLLBACK
را بزنید. قبل از شروع هر یک از دستورات داخل TRANSACTION
، شما را به شروع حالت قبلی خود برمی گرداند و تمام تغییراتی را که در آن تراکنش ایجاد کرده اید پاک می کند.
فکر کنید که مانند Git
است: شما همه تغییرات پایگاه داده خود را انجام داده اید و اکنون می خواهید آنها را به شعبه خود (در مورد ما یک پایگاه داده) متعهد کنید. یا، میتوانید با نادیده گرفتن تغییرات خود (در مورد ما با فراخوانی .Rollback()
"برگرداندن" آنها را انتخاب کنید.
try { await connection.ExecuteAsync(createSQL, transaction: transaction); // Check if the Customer table exists var tableExists = await connection.QueryFirstOrDefaultAsync<int>( "SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='Customer';", transaction: transaction); if (tableExists > 0) { // Table exists and populated, no need to seed database again return true; } await connection.ExecuteAsync(insertSQL, transaction: transaction); // Commit the transaction if everything is successful transaction.Commit(); connection.Close(); return true; } catch (Exception ex) { Console.WriteLine(ex.Message); // An error occurred, rollback the transaction transaction.Rollback(); connection.Close(); return false; } }
قرار دادن منطق فرمان در داخل یک بلوک try/catch
به ما امکان میدهد تا زمان وقوع بازگشت تراکنش را کنترل کنیم. در حالی که SQLite به طور خودکار تراکنشها را در صورت عدم اطلاع از Commit
برمیگرداند و یک استثنا ایجاد کرده است، برخی از سیستمهای پایگاه داده ممکن است در صورت بروز خطا، تراکنش را بهطور خودکار برگردانند یا نکنند - این تضمین جهانی نیست.
من شخصاً ترجیح میدهم صراحتاً به تعهد و بازگرداندن تراکنشهای خودم رسیدگی کنم، تا این کنترل را بر آنچه که اتفاق میافتد و نمیشود داشته باشم.
کد transaction.Commit()
به پایگاه داده اطلاع می دهد تا تراکنش را انجام دهد - یعنی تمام تغییراتی که در پایگاه داده انجام شده است.
مرحله 3: روش InitializeDBAsync را به فایل Program.cs اضافه کنید
به فایل Program.cs
خود برگردید و روش InitializeDBAsync
را قبل از app.Run()
فراخوانی کنید:
// Initialise the Db await app.InitializeDBAsync(); app.Run();
برنامه را دوباره اجرا کنید...
یک بار دیگر در ترمینال خود در پوشه FCC_Dapper
اجرا کنید:
dotnetrun --launch-profile https
اکنون برنامه شما را می سازد و اجرا می کند، جدول customer
را در فایل customers.db
شما راه اندازی می کند و داده ها را تخمین می زند.
CRUD - ایجاد، خواندن، به روز رسانی، حذف
قبل از اینکه به نحوه نوشتن متدهای CRUD بپردازیم، اجازه دهید به اختصار چند روش مفید و متداول توسعه Dapper را تحلیل کنیم.
QueryAsync<T>()
- یک پرس و جو را اجرا می کند و مجموعه نتایج را به یک فهرست با تایپ قوی از اشیاء نوع T نگاشت می کند.
QueryFirstOrDefaultAsync<T>
– شبیه Query<T>
است، اما اگر هیچ نتیجه ای یافت نشد، اولین نتیجه یا مقدار پیش فرض را برمی گرداند.
ExecuteAsync()
- یک عبارت SQL غیر پرس و جو را اجرا می کند (به عنوان مثال، INSERT، UPDATE، DELETE) و تعداد ردیف های تحت تأثیر را برمی گرداند.
QueryMultiple()
- یک پرس و جو را اجرا می کند که چندین مجموعه نتیجه را برمی گرداند و به شما امکان می دهد چندین مجموعه از نتایج را از یک پرس و جو بخوانید.
QuerySingle<T>:
- یک پرس و جو را اجرا می کند و دقیقاً یک نتیجه را انتظار دارد. اگر صفر یا بیش از یک نتیجه وجود داشته باشد، یک استثنا می اندازد.
چگونه یک مخزن برای روش های خام بسازیم
برای این نسخه ی نمایشی، من یک مخزن سریع با استفاده از الگوی مخزن تهیه کرده ام که می توانید در این مقاله درباره آن اطلاعات بیشتری کسب کنید.
گزینه 1: همه فایل ها و پوشه ها را به صورت دستی در IDE / Code Editor ایجاد کنید
ICustomerRepository.cs
IGenericRepository.cs
IUnitOfWork.cs
UnitOfWork.cs
CustomerRepsository.cs
گزینه 2 (توصیه می شود): فایل هایی را با استفاده از شروع CLI در پوشه FCC_Dapper
خود در ترمینال ایجاد کنید.
خط فرمان ویندوز:
سیستم عامل مک:
امیدوارم در این مرحله شما ارزش و کارایی استفاده از CLI را ببینید.
پس از ایجاد این فایل ها، کد زیر را در هر یک از آنها قرار دهید:
این مورد بعدی مهمترین است، زیرا این کلاس با تمام منطق واقعی است:
using Dapper; using FCC_Dapper; using Microsoft.Data.Sqlite; public class CustomerRepository : ICustomerRepository { public CustomerRepository(IConfiguration configuration) { this._configuration = configuration; } private readonly IConfiguration _configuration; public async Task<int> AddAsync(Customer customer) { var sql = "INSERT INTO Customer (firstName, lastName, email, dob) VALUES (@FirstName, @LastName, @Email, @DOB)"; using var connection = new SqliteConnection(_configuration.GetConnectionString("DefaultConnection")); return await connection.ExecuteAsync(sql, customer); } public async Task<int> DeleteAsync(int id) { var sql = "DELETE FROM Customer WHERE ID = @ID"; using var connection = new SqliteConnection(_configuration.GetConnectionString("DefaultConnection")); var result = await connection.ExecuteAsync(sql, new { ID = id }); return result; } public async Task<IReadOnlyList<Customer>> GetAllAsync() { var sql = "SELECT * FROM Customer"; using var connection = new SqliteConnection(_configuration.GetConnectionString("DefaultConnection")); var result = await connection.QueryAsync<Customer>(sql); return result.ToList(); } public async Task<Customer?> GetByIdAsync(int id) { var sql = "SELECT * FROM Customer WHERE ID = @ID"; using var connection = new SqliteConnection(_configuration.GetConnectionString("DefaultConnection")); var result = await connection.QuerySingleOrDefaultAsync<Customer>(sql, new { ID = id }); return result ?? null; } public async Task<int> UpdateAsync(Customer entity) { var sql = "UPDATE Customer SET FirstName = @FirstName, LastName = @LastName, DOB = @DOB, Email = @Email WHERE ID = @ID"; using var connection = new SqliteConnection(_configuration.GetConnectionString("DefaultConnection")); var result = await connection.ExecuteAsync(sql, entity); return result; } }
در این کلاس CustomerRepository
، ما چندین روش مختلف را راهاندازی میکنیم که میتوان آنها را در هر جایی از WebApi فراخوانی کرد.
با انتزاع منطق دستورات عمل بر روی یک منبع داده، اگر بخواهیم از Dapper به EF (Entity Framework)، یکی دیگر از معروفترین .Net ORM، تغییر کنیم، بقیه برنامه همچنان کار میکند.
هر روش بسیار ساده است و اقدامات زیر را انجام می دهد:
رشته اتصال را بازیابی می کند
یک SQLiteConnection می سازد
یک رشته SQL می سازد
SQL را با استفاده از شی اتصال اجرا می کند.
چند تفاوت جزئی وجود دارد. ممکن است متوجه شده باشید که برخی از نحوها دارای نمادهای @
در جلوی نام متغیرها هستند. این مکانها برای پرس و جوهای پارامتری شده هستند.
نگاهی کوتاه به SQL Injection
Dapper پارامترسازی داخلی کوئری ها را ارائه می دهد، به این معنی که می تواند از شما در برابر حملات SQL Injection محافظت کند.
همانطور که در مقاله پیوند داده شده در بالا توضیح داده شد، می توانید یک متغیر رشته ای را برای ساخت SQL خود تعریف کنید:
با این حال، اتفاقی که در اینجا افتاده این است که یک کاربر ممکن است به طور مخرب یک نام و یک دستور SQL را در ورودی فرم برای FirstName
وارد کرده باشد تا محافظت از تزریق SQL برنامه وب شما را آزمایش کند.
اگر به سادگی مقدار را بدون هیچ گونه پاکسازی دریافت کنید، دستور SQL آنها را به رشته پرس و جوی SQL خود تزریق می کنید، و همانطور که می دانیم ;
کاراکتر دستورات SQL را جدا می کند.
معنی آنچه در واقع اتفاق خواهد افتاد این است:
Dapper پرس و جوهای پارامتری شده را ترجیح می دهد، به این معنی که می توانید متغیرهای پارامتر را به رشته SQL خود منتقل کنید، و سپس با متغیر واقعی که از یک موجودیت یا شی ناشناس به آن اختصاص داده شده است، جایگزین می شوند.
بیایید با استفاده از یک پرس و جوی پارامتری به مثال بالا نگاه کنیم:
با استفاده از یک شی ناشناس و انتساب ویژگی Username
به مقدار userInput
، کوئری را پارامتری کرده ایم.
به این معنی که خروجی کوئری SQL به این صورت خواهد بود:
تفاوت عمده در اینجا این است که معیارهای جستجوی username
اکنون به جای یک دستور SQL مانند مقدار جستجوی ستون در نظر گرفته شده است. این سطح حفاظت بیشتری را ارائه می دهد. روشهای زیادی برای محافظت در برابر تزریق SQL وجود دارد، اما اینها برای این آموزش نیستند.
تطبیق پارامتر
همانطور که ممکن است از کدی که استفاده می کنیم متوجه شده باشید، ما از یک شی ناشناس استفاده نمی کنیم - ما به طور مستقیم به نهاد customer
خود منتقل می کنیم. سپس Dapper پارامترها و مکاننمای آنها (مقادیر @
) را به نام ویژگی موجودیت نگاشت میکند.
تزریق وابستگی - ثبت رابط ها
یک فایل به نام ServiceRegistration.cs
در پوشه اصلی پروژه ایجاد کنید و کد زیر را اضافه کنید:
این کلاس یک متد افزونه به IServiceCollection به نام AddInfrastructure()
اضافه می کند که به ما این امکان را می دهد تا به راحتی کلاس های Repository و UnitOfWork خود را برای Dependency Injection اضافه کنیم.
سپس می توانیم این را در فایل Program.cs
خود فراخوانی کنیم:
using Dapper; using FCC_Dapper; using Microsoft.Data.Sqlite; var builder = WebApplication.CreateBuilder(args); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); //Add the line here builder.Services.AddInfrastructure(); var app = builder.Build();
اتصال مخزن به نقاط پایانی Web API
ما از نقاط پایانی API زیر استفاده خواهیم کرد:
/get
/get/{id}
/create
/update
/delete
مرحله 1: با استفاده از حداقل API، نقاط پایانی را به Program.cs
اضافه کنید.
پس فایل Program.cs
شما اکنون باید به این شکل باشد:
using Dapper; using FCC_Dapper; using Microsoft.Data.Sqlite; var builder = WebApplication.CreateBuilder(args); // Add services to the container. // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); builder.Services.AddInfrastructure(); var app = builder.Build(); // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } app.UseHttpsRedirection(); // initialize the database here await app.InitializeDBAsync(); app.Run(); app.MapGet("/get", async (IUnitOfWork unitOfWork) => { var customers = await unitOfWork.Customers.GetAllAsync(); return Results.Ok(customers); }); app.MapGet("/get/{id}", async (IUnitOfWork unitOfWork, int id) => { var data = await unitOfWork.Customers.GetByIdAsync(id); return data != null ? Results.Ok(data) : Results.Problem("Failed to locate customer"); }); app.MapPost("/create", async (IUnitOfWork unitOfWork) => { var count = await unitOfWork.Customers.AddAsync(new Customer { FirstName = "Stan", LastName = "Lee", DOB = new DateTime(1945, 1, 1), Email = "stan.lee@test.com" }); return count > 0 ? Results.Ok("Customer created successfully") : Results.Problem("Unable to create customer"); }); app.MapPut("/update", async (IUnitOfWork unitOfWork, Customer customer) => { var count = await unitOfWork.Customers.UpdateAsync(customer); return count > 0 ? Results.Ok("Customer updated successfully") : Results.Problem("Customer failed to update"); }); app.MapPost("/delete", async (IUnitOfWork unitOfWork, int id) => { var count = await unitOfWork.Customers.DeleteAsync(id); return count > 0 ? Results.Ok("Customer deleted successfully") : Results.Problem("Customer failed to delete"); });
کد بالا این است:
راه اندازی هر یک از نقاط پایانی با استفاده از حداقل API
وابستگی تزریق یک کلاس UnitOfWork
به هر نقطه پایانی
فراخوانی متد مربوطه در CustomerRepository
برگرداندن یک پاسخ HTTP بر اساس معیارهای خاصی که از مخزن بازگردانده شده است.
اینجاست که می توانید قدرت واقعی انتزاع CustomerRepository
را به دور از منطق تجاری برنامه از طریق UnitOfWork
ببینید. فایل Program.cs
مربوط به ابزار پایگاه داده ای است که ما از آن استفاده می کنیم، تنها چیزی که به آن اهمیت می دهد رسیدگی به تماس با کنترل کننده مربوطه و مدیریت پاسخ به مشتری است.
نحوه تست API
می توانید API را با اجرای دستور قبلی آزمایش کنید:
dotnet run --launch-profile https
سپس وقتی مرورگر شما باز شد، به /swagger
بروید و اکنون خواهید دید:
ادامه دهید، با API بازی کنید. سعی کنید به تمام نقاط پایانی در رابط کاربری Swagger ضربه بزنید.
برای مثال امتحان کنید:
نقطه پایان ایجاد را بزنید
نقطه پایانی /get
بزنید و تحلیل کنید که مشتری «Stan Lee» تازه ایجاد شده شما در پایین فهرست شده است
نقطه پایانی /update
را بزنید، شاید نام یا ایمیل او را به روز کنید
نقطه پایانی /delete
در شناسه مشتری تازه ایجاد شده Stan Lee و سپس /getById
با همان شناسه ضربه بزنید و ببینید چه اتفاقی می افتد.
کلمات پایانی
در اینجا شما آن را دارید - یک Api وب دات نت کاملاً کارآمد، با پشتیبانی Dapper و یک فایل پایگاه داده محلی SQLite، با استفاده از یک پیاده سازی اساسی از الگوی مخزن.
در این آموزش یاد گرفتید:
چگونه Dapper را به پروژه اضافه کنیم
مزایا و معایب Dapper در مقایسه با رقبای خود
روش ها و برنامه های گفت نی پایه Dapper
آشنایی با تراکنش های SQL و پیشگیری از تزریق SQL
پرس و جوهای پارامتری شده
امیدوارم این دوره برای شما مفید بوده باشد، و مثل همیشه هر سوالی دارید، لطفاً با من تماس بگیرید یا من را در Twitter / X دنبال کنید.
ارسال نظر