مقدمه برنامه نویسی سه لایه در سی شارپ
امروزه با پیشرفت تکنولوژی و گسترش استفاده از دنیای دیجیتال، نیاز به توسعه همواره صفحات وب یا نرم افزارهای کاربردی کاملاً مشهود است. فرض کنید با یک دانش آموزی سال آخری طرف هستید که در آزمون کنکور شرکت خواهد کرد. مشاورش ساعات خاصی از صبح را برای مطالعه دروس تخصصی، ظهرها را برای دروس عمومی و بعدازظهرها را برای مطالعه چکیدههای مطالعاتش اختصاص داده است. حال در این شرایط که مبحث زمان بسیار مهم است؛ اگر آن دانش آموز کتابها و جزواتش را در یک گوشه جمع کند؛ راحتتر بههرکدامشان دسترسی خواهد داشت یا اگر آنها را مرتب در قفسههای کتاب بچیند؟
مسلم است که این دانش آموز اگر کتابهایش را در یک کمد بهصورت منظم، قفسهبندی کند؛ راحتتر به منابع درسی خود دسترسی خواهدداشت و اگر بهجزوه خاصی نیاز داشته باشد هم نهتنها در زمانش صرفه جویی شده؛ بلکه خسته یا سردرگم هم نمیشود.
در مورد معماری سه لایه هم دقیقاً روش کار به این صورت است که برنامه نویس با لایهبندی تکه کدها، کار رابرای دسترسیهای بعدی آسانتر میکند. پس اگر من از شما بپرسم که چرا معماری سه لایه بهترین روش برنامه نویسی است؟! احتمالاً در پاسخ جواب خواهید داد که این روش امکان بازبینی و ویرایش سادهتر کدها را فراهم کرده و از سردرگمی برنامهنویس جلوگیری میکند.
مزایای معماری سه لایه در برنامه نویسی سه لایه در سی شارپ
قبل از شروع برنامه نویسی سه لایه در سی شارپ، آگاهی از مزایای این روش، لازم و ضروری است. لذا جدول زیر، برای این منظور تهیه شده است.
مزایای برنامه نویسی سه لایه | توضیحات |
کدنویسی واضح | برنامه نویسی به جای اینکه کدها را پشت سرهم بنویسد؛ آنها را به طور منظم دسته بندی میکند. |
توسعه و نگهداری آسان | با توجه به جدا بودن وظایف هر کدام از لایهها، تغییر و اصلاح هر لایه به راحتی و بدون تحت تاثیر قرار دادن کل برنامه انجام خواهد گرفت |
انتقال آسان با صرفه جویی در زمان | در زمان انتقال برنامه به دیگران، با توجه به استاندارد بودن معماری آن، در زمان و هزینه صرفه جویی میشود. |
توزیع آسان توابع و پارامترها | با سازماندهی کد در لایههای مختلف بر اساس مسئولیت هر تابع، تیم توسعه میتواند کدهای مربوط به خود را بر روی هر لایه به طور مستقل پیاده سازی کند. به این ترتیب، حجم تیم کاری کنترل میشود. |
امنیت بیشتر | با توجه به اینکه دسترسیها مستقیم نیست، امنیت سیستم بیشتر خواهد بودد. |
پاورپوینت آماده پاورپوینت شی گرایی در سی شارپ در ۱۸ اسلاید با فرمت pptx. با قابلیت ویرایش برای ارائه درسی آماده دانلود میباشد. زبان برنامه نویسی سی شارپ #C یکی از قدرتمندترین زبانهای برنامه نویسی شی گرا است که در این پاورپوینت به معرفی قابلیت شی گرایی در سی شارپ پرداخته شده است. برای دسترسی به این فایل روی لینک زیر کلیک کنید.
پیشنیازهای برنامه نویسی سه لایه در سی شارپ
قبل از اینکه شروع کنیم به آموزش برنامه نویسی سه لایه در سی شارپ؛ بهتر است که با تئوری N-tier آشنا شوید. بر طبق این نظریه، برنامه نویس میتواند به تعداد بیشمار، لایه یا ردیف ایجاد نموده و کدهای خود را در داخل این ردیفها دستهبندی کند اما در روشهای مرسوم امروز، عموماً این دستهبندی در سه لایه انجام میگیرد. در ادامه، بهتوضیح این سه لایه خواهیم پرداخت و چگونگی روش انجام کار را با استفاده از پیاده سازی یک برنامه کاربردی بهشما خواهیم آموخت.
۱- لایه ارائه یا Presentation layer
اولین و بالاترین لایه در یک برنامه کامپیوتری لایه ارائه یا User Interface معروف بهلایه رابط کاربر، است و وظیفه اعتبارسنجی ورودی کاربر و بهدنبال آن پردازش قوانین را بر عهده دارد. بهعنوان مثال در صفحات وب، فرمهای وب و در ویندوزها، فرمهای ویندوز همان لایه UI هستند. در واقع، عملیات مربوط به هر فرمی که باید توسط کاربران در ورود یا در هر مرحله از استفاده یک اپلیکیشن یا وبسایت تکمیل شود؛ در لایه UI انجام میگیرد.
۲- لایه کسب و کار Business Layer
لایه کسب و کار که به Business Layer معروف است؛ وظیفه پردازش انواع مختلف عملیات تجاری را برعهده دارد. کلاسها و موجودیتهای تجاری در این لایه تعریف میشوند و در بیان دیگر، پس از اینکه لایه ارائه، دادههای ورودی را جمع آوری کرد؛ آنها را به لایه کسب و کار تحویل میدهد تا دادههای فرم، با قوانین تجاری سفارشی تأیید و پردازش شوند.
۳- لایه دسترسی به داده Data Access Layer
لایه Data Access Layer همان لایهای است که بعد از لایه کسب و کار قرار میگیرد و وظیفه برقراری ارتباط بین پایگاه داده و Business Layer را بر عهده دارد. بیشتر برنامه نویسان و طراحات صفحات وب، از لایه دسترسی به داده، برای واکشی دادهها از فروشندگان مختلف پایگاه داده استفاده میکنند. در این روش Data Access Layer، مستقل از پلتفرم عمل کرده و در هنگام ارتقای نرم افزاری یا بروزرسانی، برنامه نویس بهآسانی تغییرات را بر روی آن اعمال میکند.
نحوه انتقال دادهها از یک لایه به لایه دیگر در معماری سه لایه
دوستان عزیز، باید بهعرضتان برسانم که روشهای مختلفی برای این کار وجود دارد؛ به نظر من بهترین کار این است که با استفاده از پارامترهای تابع، دادههای مربوط به هر عملیات را از یک لایه به لایه دیگر انتقال دهید.
مثال کاربردی
برای آموزش بهتر شما عزیزان، تصمیم گرفتیم یک دمو بر اساس الگوی Repository + UnitOfWork، با اجزای زیر در محیط برنامه نویسی سی شارپ بسازیم و صفر تا صد آن را پیاده سازی کنیم. با آموزش این مطلب، ساخت و استقرار یک برنامه سه لایه، برایتان به آسانی آب خوردن خواهدبود. برای شروع، اجزای زیر را درنظر بگیرید.
- برای Business Objects یا اصطلاحاً اشیای تجاری، برای لایه دسترسی به داده و لایه منطق تجاری: کتابخانه کلاس NET Core : این لایه اشیایی را در خود جای میدهد که در داخل برنامه و توابع کمکی، بدون در نظر گرفتن منطق هر کدام، برای همه لایهها مورد استفاده قرار میگیرند. در معماری سه لایه، لایه Business Objects اختیاری است ولی همواره باید درنظر داشته باشیم که کدهای تکراری را تا حد امکان کاهش داده و استفاده از یک لایه برای حفظ کدهای رایج را بهعنوان یک ضرورت درنظر بگیریم.
- برای لایه ارائه یا لایه UI: صفحات ASP.NET Core 5.0 Razor
در شکل زیر، نحوه طراحی برنامه موردنظر، به مرحله تصویر درآمده است.
قدم اول- تشکیل پایگاه داده مناسب
اولین قدم تهیه یک دیتابیس با مشخصات زیر است. وظیفه دیتابیس ذخیره سازی دادههای برنامه است. بر اساس اطلاعاتی که در دست دارید؛ سطرها و ستون جداول Sql متفاوت خواهد بود. به تصاویر زیر توجه کنید.
دوستان عزیز، در این مرحله، برای اتصال به پایگاه داده از Entity Framework Core استفاده کنید. پس از اینکه این کار را انجام دادید؛ نیاز است یک Mapping از پایگاه داده به برنامه خود ایجاد نمایید. برای این منظور، ایتدا یک کنسول Package Manager را برای لایه کسب و کار باز کنید. سپس دستور Scaffolding را اجرا کرده و SERVER ،DATABASE ،USER ،PASSWORD را با مقادیر مناسب بر اساس تنظیمات SQL Server خود جایگزین کنید. به تکه کد زیر، توجه کنید.
Scaffold-DbContext "Data Source=SERVER;Initial Catalog=DATABASE;Persist Security Info=True;User ID=USER;Password=PASSWORD;MultipleActiveResultSets=True" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Entities -ContextDir Context -Context DemoContext -f
پس از اجرای تکه کد بالا، جداول پایگاه داده، باید در قسمت کدهای Name entities برنامه ایجاد شوند. از آنجایی که موجودیتها میتوانند در همه لایهها بدون هیچ پایگاه داده یا منطق تجاری استفاده شوند؛ باید آنها را در لایه کسب و کار قرار دهیم.
تذکر مهم: در ادامه، یک کلاس DataContext با نام “DemoContext” هم خواهیم ساخت. این کلاس دسترسی به پایگاه داده را فراهم میکند؛ بنابراین باید در لایه Data Access قرار گیرد. به تصاویر زیر توجه کنید.
عزیزان، در نظر داشته باشید که همه موجودیتهای داده باید دارای اقدامات CRUD باشند؛ بنابراین نیاز است دو رابط عمومی برای این منظور در نظر بگیرید. پس بیایید اولاً یک رابط عمومی به نام IRepository ایجاد کنیم تا مخزن هر موجودیت بهوسیله این رابط پیادهسازی شود. کدهای مربوط به ایجاد رابط عمومی در ادامه آورده شده است.
using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; namespace ThreeLayerSample.Domain.Interfaces { public interface IRepository where T : class { DbSet Entities { get; } DbContext DbContext { get; } Task<IList> GetAllAsync(); T Find(params object[] keyValues); Task FindAsync(params object[] keyValues); Task InsertAsync(T entity, bool saveChanges = true); Task InsertRangeAsync(IEnumerable entities, bool saveChanges = true); Task DeleteAsync(int id, bool saveChanges = true); Task DeleteAsync(T entity, bool saveChanges = true); Task DeleteRangeAsync(IEnumerable entities, bool saveChanges = true); } }
بههمین ترتیب، یک رابط عمومی دیگر بهنام IUnitOfWork هم ایجاد خواهد شد که این تکلیف را بر عهده شما عزیزان قرار میدهم.
قدم دوم- لایه دسترسی بهداده
با اعمال الگوهای طراحی مخزن عمومی و واحد کار، کلاسهای لایه دسترسی بهداده به صورت زیر پیاده سازی میشوند. با اجرای تکه کد زیر، اولین قدم در طراحی لایه دسترسی بهداده را برداشتهاید.
using System.Collections.Generic; using System.Data; using System.Threading; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Storage; using ThreeLayerSample.Domain.Interfaces; namespace ThreeLayerSample.Infrastructure { public class UnitOfWork : IUnitOfWork { public DbContext DbContext { get; private set; } private Dictionary<string, object> Repositories { get; } private IDbContextTransaction _transaction; private IsolationLevel? _isolationLevel; public UnitOfWork(DbFactory dbFactory) { DbContext = dbFactory.DbContext; Repositories = new Dictionary<string, dynamic>(); } public async Task SaveChangesAsync(CancellationToken cancellationToken = default) { return await DbContext.SaveChangesAsync(cancellationToken); } private async Task StartNewTransactionIfNeeded() { if (_transaction == null) { _transaction = _isolationLevel.HasValue ? await DbContext.Database.BeginTransactionAsync(_isolationLevel.GetValueOrDefault()) : await DbContext.Database.BeginTransactionAsync(); } } public async Task BeginTransaction() { await StartNewTransactionIfNeeded(); } public async Task CommitTransaction() { await DbContext.SaveChangesAsync(); if (_transaction == null) return; await _transaction.CommitAsync(); await _transaction.DisposeAsync(); _transaction = null; } public async Task RollbackTransaction() { if (_transaction == null) return; await _transaction.RollbackAsync(); await _transaction.DisposeAsync(); _transaction = null; } public void Dispose() { if (DbContext == null) return; if (DbContext.Database.GetDbConnection().State == ConnectionState.Open) { DbContext.Database.GetDbConnection().Close(); } DbContext.Dispose(); DbContext = null; } public IRepository Repository() where TEntity : class { var type = typeof(TEntity); var typeName = type.Name; lock (Repositories) { if (Repositories.ContainsKey(typeName)) { return (IRepository) Repositories[typeName]; } var repository = new Repository(DbContext); Repositories.Add(typeName, repository); return repository; } } } }
حال یه یک کلاس DbFactory نیاز داریم که DbContext را هنگام استفاده از آن، مقداردهی اولیه کند.
using System.Collections.Generic; using System.Threading.Tasks; using ThreeLayerSample.Domain.Entities; namespace ThreeLayerSample.Domain.Interfaces.Services { public interface IWorkService { Task<IList> GetAll(); Task GetOne(int workId); Task Update(Work work); Task Add(Work work); Task Delete(int workId); } }
قدم سوم- لایه کسب و کار
اولین مرحله برای ایجاد لایه کسب و کار، ساختن یک رابط سرویس است. دوستان عزیز، برای این منظور بیایید برای WorkService یک رابط وابسته بهلایه Presentation بسازیم. برای درک بهتر آنچه گفته شد؛ کدهای زیر را بررسی کنید.
using System; using System.Collections.Generic; using System.Threading.Tasks; using ThreeLayerSample.Domain.Entities; using ThreeLayerSample.Domain.Interfaces; using ThreeLayerSample.Domain.Interfaces.Services; namespace ThreeLayerSample.Service { public class WorkService: IWorkService { private readonly IUnitOfWork _unitOfWork; public WorkService(IUnitOfWork unitOfWork) { _unitOfWork = unitOfWork; } public async Task<IList> GetAll() { return await _unitOfWork.Repository().GetAllAsync(); } public async Task GetOne(int workId) { return await _unitOfWork.Repository().FindAsync(workId); } public async Task Update(Work workInput) { try { await _unitOfWork.BeginTransaction(); var workRepos = _unitOfWork.Repository(); var work = await workRepos.FindAsync(workInput.Id); if (work == null) throw new KeyNotFoundException(); work.Name = work.Name; await _unitOfWork.CommitTransaction(); } catch (Exception e) { await _unitOfWork.RollbackTransaction(); throw; } } public async Task Add(Work workInput) { try { await _unitOfWork.BeginTransaction(); var workRepos = _unitOfWork.Repository(); await workRepos.InsertAsync(workInput); await _unitOfWork.CommitTransaction(); } catch (Exception e) { await _unitOfWork.RollbackTransaction(); throw; } } public async Task Delete(int workId) { try { await _unitOfWork.BeginTransaction(); var workRepos = _unitOfWork.Repository(); var work = await workRepos.FindAsync(workId); if (work == null) throw new KeyNotFoundException(); await workRepos.DeleteAsync(work); await _unitOfWork.CommitTransaction(); } catch (Exception e) { await _unitOfWork.RollbackTransaction(); throw; } } } }
در مرحله بعد، منطق تجاری را برای پردازش سرویس Work در کلاسی به نام WorkService پیاده سازی کرده و این کلاس را در لایه Business Logic قرار دهید. پیاده سازی شما باید بهگونهای باشد که سرویس در طی یک درخواست، تمام آیتمهای جدول Work را بهوسیله نمونه مخزن موجودیت Work که در مخزن عمومی پیاده سازی شده است را دریافت کند.
قدم چهارم- لایه ارائه
برای ایجاد لایه ارائه اولاً یک برنامه در صفحه ASP.NET Core Razor ایجاد کنید. ثانیاً پس از ایجاد برنامه، با استفاده از تکه کد زیر، یک کلاس ServiceCollectionExtensions در پوشه Extensions بسازید.
using System; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using ThreeLayerSample.Domain.Interfaces; using ThreeLayerSample.Domain.Interfaces.Services; using ThreeLayerSample.Domain.Models; using ThreeLayerSample.Infrastructure; using ThreeLayerSample.Service; namespace ThreeLayerSample.Web_Razor_.Extensions { public static class ServiceCollectionExtensions { public static IServiceCollection AddDatabase(this IServiceCollection services, IConfiguration configuration) { services.AddDbContext(options => { options.UseSqlServer(AppSettings.ConnectionString, sqlOptions => sqlOptions.CommandTimeout(120)); options.UseLazyLoadingProxies(); } ); services.AddScoped<Func>((provider) => () => provider.GetService()); services.AddScoped(); services.AddScoped<IUnitOfWork, UnitOfWork>(); return services; } public static IServiceCollection AddServices(this IServiceCollection services) { return services.AddScoped<IWorkService, WorkService>(); } } }
در ادامه، رشته اتصال برنامه را به فایل appsettings.json اضافه کنید. با استفاده از این رشته اتصال، لایه دسترسی به داده با پایگاه داده ارتباط سازنده خود را برقرار میکند.
{ "Logging": { "LogLevel": { "Default": "Information", "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information" } }, "AppSettings": { "ConnectionString": "Data Source=(local);Initial Catalog=ThreeLayerSample.Database;Persist Security Info=True;User ID=sa;Password=PASSWORD;MultipleActiveResultSets=True" }, "AllowedHosts": "*" }
پس از اجرای تکه کد بالا، ضروری است که یک کلاس AppSettings را بهصورت زیر، بهلایه Entity اضافه کنیم.
namespace ThreeLayerSample.Domain.Models { public class AppSettings { public static string ConnectionString { get; private set; } } }
دوستان عزیز، حال رسیدیم بهکدنویسی قسمت Startup برنامه موردنظر؛ نگران نباشید! در ادامه آموزش تکه کدهای مربوط بهآن را هم آوردهام. ابتدا فایل Startup.cs را باز کنید. سپس کدهای زیر را اضافه کنید. بهاین ترتیب که در کدنویسی خود، اولاً دادهها را از appsettings.json بخوانید. سپس آنها را در کلاس AppSettings ایجاد شده، ذخیره کرده و در ConfigureServices نمونههایی را برای DataContext ،Factory ،UnitOfWork و WorkService در برنامه ثبت کنید.
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using System; using ThreeLayerSample.Domain.Models; using ThreeLayerSample.Web_Razor_.Extensions; namespace ThreeLayerSample.Web_Razor_ { public class Startup { public Startup(IWebHostEnvironment env) { Configuration = InitConfiguration(env); } public IConfiguration Configuration { get; } public void ConfigureServices(IServiceCollection services) { services.AddDatabase(Configuration) .AddServices(); services.AddRazorPages(); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Error"); app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapRazorPages(); }); } private IConfiguration InitConfiguration(IWebHostEnvironment env) { configuration.GetSection("AppSettings").Get(options => options.BindNonPublicProperties = true); return configuration; } } }
بههمین ترتیب در ادامه، فایل Index.cshtml.cs را باز کرده؛ WorkService را بهآن اضافه کنید. سپس دادهها را از WorkService دریافت کرده و آن را روی ویژگی Works قرار دهید. در انتها، با استفاده از آموزشهایی که داده شد؛ در فایل Index.cshtml کدهای مربوط بهارائه داده به رابط کاربری را اضافه نمایید. چنانچه تصمیم به ارائه کلاسی و آکادمیک در این زمینه هستید میتوانید از فایل پاورپوینت آماده موجود در مجموعه پی استور بهره مند شوید جهت دسترسی به این فایل به لینک زیر رجوع کنید.
سخن آخر در رابطه با آموزش برنامه نویسی سه لایه در سی شارپ
دوستان و همراهان همیشگی مجموعه پی استور، هر کدام از شما با کامل کردن کدهای بالا و پیاده سازی آن میتوانید یک دموی آزمایشی بهروش معماری سه لایه بسازید و با اعمال تغییرات در بخش پایگاه داده، نتایج را مورد بررسی قرار دهید. امیدوارم که این آموزش برنامه نویسی سه لایه در سی شارپ مفید واقع شود و نتایج یا حتی تجربیات خود را با ما درمیان بگذارید. موفق و پیروز باشید.