<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
    <channel>
        <title>نوشته های رضا منصوری</title>
        <link>https://virgool.io/feed/@reza.mansouri</link>
        <description>توسعه دهنده نرم‌افزار</description>
        <language>fa</language>
        <pubDate>2026-06-16 10:49:09</pubDate>
        <image>
            <url>https://files.virgool.io/upload/users/1132871/avatar/DTlswM.jpeg?height=120&amp;width=120</url>
            <title>رضا منصوری</title>
            <link>https://virgool.io/@reza.mansouri</link>
        </image>

                    <item>
                <title>پیاده سازی پروژه API در دات نت ، قسمت ششم ( قسمت آخر ) ، عملیات CRUD در RestAPI پارت دوم</title>
                <link>https://virgool.io/@reza.mansouri/%D9%BE%DB%8C%D8%A7%D8%AF%D9%87-%D8%B3%D8%A7%D8%B2%DB%8C-%D9%BE%D8%B1%D9%88%DA%98%D9%87-api-%D8%AF%D8%B1-%D8%AF%D8%A7%D8%AA-%D9%86%D8%AA-%D9%82%D8%B3%D9%85%D8%AA-%D9%BE%D9%86%D8%AC%D9%85-%D9%82%D8%B3%D9%85%D8%AA-%D8%A2%D8%AE%D8%B1-%D8%B9%D9%85%D9%84%DB%8C%D8%A7%D8%AA-crud-%D8%AF%D8%B1-restapi-%D9%BE%D8%A7%D8%B1%D8%AA-%D8%AF%D9%88%D9%85-js41fchikfgy</link>
                <description>سلام :)قسمت های قبلی رو اینجا میتونید ببینید.پیاده سازی پروژه API در دات نت ، قسمت اول ، معماری پیاز (Onion Architecture)پیاده سازی پروژه API در دات نت ، قسمت دوم ، ApplicationServices و DataLayerپیاده سازی پروژه API در دات نت ، قسمت سوم ، EndPoint توسط RestAPIپیاده سازی پروژه API در دات نت ، قسمت چهارم ، RestAPI Authentication Jwtپیاده سازی پروژه API در دات نت ، قسمت پنجم ، عملیات CRUD در RestAPIالبته هنوز از کارمون مونده ولی سورس کامل این مقالات رو میتونید از اینجا ببینید. تا اینجای کار سرویس UserExamService رو کامل کردیم و از متد MakeExam در پروژه ی Api در کنترلر ExamController استفاده کردیم و تست هم انجام دادیم و میتونیم آزمون های خودمون رو اضافه (Insert) کنیم.در این مقاله ی اکشن های GetAll Update Delete رو پیاده میکنیم تا عملیات CRUD ما کامل بشه البته سرویس اونها رو داخل UserExamService.cs نوشتیم و فقط باید در کنترلر ExamController اکشن های اونها رو کامل کنیم .ابتدا با اکشن GetAll  شروع میکنیم که قرار هستش آزمون های کاربر رو به ما برگردونه .ExamController [HttpGet(&amp;quotGetAll&amp;quot)]
        [Authorize]
        public IActionResult GetAll([FromQuery] string pageNumber= &amp;quot1&amp;quot, string pageSize= &amp;quot5&amp;quot)
        {
            var result = _userexamService.GetExam( int.Parse( pageNumber), int.Parse( pageSize), getCurrentUserMobile());
            return Ok(result);
        }در این اکشن ابتدا ار HTTPGet استفاده کردیم چون قصد داریم اطلاعات رو بخونیم ، فیلتر [Authorize] هم به منظور چک کردن کاربر قبل از انجام اکشن هستش ، ورودی اکشنمون همانند ورودی متد GetExam سرویس UserExamService ، شماره صفحه و تعداد آیتم هایی که در هر صفحه میخواهیم نمایش بدیم هستش ، البته در اینجا اطلاعات ورودیمون رو از حالت [FromQuery] خوندیم که منظور خوندن اطلاعات از URL درخواست هستش .در ادامه ورودی های شماره صفحه و تعداد نمایش در هر صفحه رو به نوع Int تبدیل کرده و شماره موبایل کاربری که توکن رو ارسال کرده رو از getCurrentUserMobile میخونیم و به سرویس UserExamService ارسال میکنیم و نتیجه رو به خروجی میفرستیم. پروژه رو اجرا میکنیم تا تست کنیم ، برای تست هم از Postman استفاده میکنیم و قبل از انجام باید باید نام کاربری و پسورد رو بگیریم ، از همون یو ای Swagger استفاده میکنیم عملیات لاگین رو انجام میدیم و توکن رو دریافت میکنیم.سپس در Postman یک درخواست از نوع GET اضافه میکنیم ، آدرس اکشن و مقادیر pageNumber و pageSize رو داخل کوئری استرینگ میزاریم به شکل زیرhttps://localhost:7229/api/Exam/GetAll?pageNumber=1&amp;pageSize=5به تب Authorization میریم نوع رو Bearer Token میزاریم و توکن که در لاگین دریافت کردیم رو داخل Token میزاریم. سپس اجرا میکنیم.میبینیم که در خروجی آزمون های کاربر ارسال شده که همون لیست از مدل UserExamItem هستش ، برگشت داده میشود.میریم سراغ اکشن Update یا همون ویرایش آزمون .ExamController[HttpPut(&amp;quotUpdate&amp;quot)]
        [Authorize]
        public IActionResult Update([FromBody] EditUserExamDto editModel)
        {
            var result = _userexamService.Edit(editModel, getCurrentUserMobile());
            return Ok(result.Id);
        }در اینجا از HTTPPut  استفاده میکنیم چون عملیات ما آپدیت هستش ، ورودی ما مدل EditUserExamDto هستش که اون رو در پروژه ی ApplicationServices ، پوشه ی Src 01.Core ApplicationServices Models UserExam میسازیم.در سرویس هم همون DTO ورودیمون و شماره موبایل کاربر رو ارسال میکنیم و آی دی آزمون ویرایش شده رو  به عنوان خروجی برمیگردونیم. خب این مورد رو هم در Postman تست میکنیم. آدرسمون https://localhost:7229/api/Exam/Update هستش ار نوع PUT و در بادی درخواست محتویات یک EditUserExamDto  رو ارسال میکنیم ، به شکل زیرخب میبینیم که Update با موفقیت انجام شد و آی دی آیتم ویرایش شده به عنوان خروجی برگشت داده شد.در آخر میمونه عملیات حذف کردن که اگر خاطرتون باشه کافیه فقط فیلد IsDeleted موجودیت UserExam رو مقدار True بزاریم که همان حذف منطقی یا SoftDelete انجام میدیم ، این کار رو داخل سرویس UserExamService انجام دادیم .برای اکشن Delete در اینجا از HTTPDelete استفاده میکنیم چون عملیات ما  حذف هستش.ExamController [HttpDelete(&amp;quotDelete&amp;quot)]
        [Authorize]
        public IActionResult Delete([FromQuery] int Id)
        {
            var result = _userexamService.Delete(Id, getCurrentUserMobile());
            return Ok(result);
        }در ورودی آی دی آیتمی که میخوایم حذف کنیم رو از کوئری استرینگ در URL میخونیم و توسط متد Delete سرویس UserExamService ، حذف ما انجام میشه ، البته در سرویس این مورد رو چک کردیم که آزمونی که قراره حذف بشه برای همون کاربری باشه که توکن فرستاده ، در خروجی هم یک مقدار True یا False به عنوان تنیجه برمیگردونیم. خب این مورد رو هم تست کنیم ، آدرس و آی دی آیتمی که میخوایم حذف کنیم رو در آدرس وارد میکنیم .https://localhost:7229/api/Exam/Delete?Id=1 نوع عملیات هم Delete هستش ، سپس توکن رو در قسمت Authorization  وارد میکنیم و درخواست رو ارسال میکنیم.خروجی ما مقدار True هستش و آیتم از دیتا بیس نیز حذف شد.در نهایت ما از متد های GetAll Update Delete در قبلتر در سرویس UserExamService نوشته بودیم  داخل پروژه ی EndPoint که یک RestApi بود استفاده کردیم .خب اینجا کارمون تموم میشه و میتونید سورس کامل این مقالات رو میتونید از اینجا ببینید.ممنون از وقتی که گذاشتید و امیدوارم از این مطالب خوشتون اومده باشه و به دردتون بخوره :)پیاده سازی پروژه API در دات نت ، قسمت اول ، معماری پیاز (Onion Architecture)پیاده سازی پروژه API در دات نت ، قسمت دوم ، ApplicationServices و DataLayerپیاده سازی پروژه API در دات نت ، قسمت سوم ، EndPoint توسط RestAPIپیاده سازی پروژه API در دات نت ، قسمت چهارم ، RestAPI Authentication Jwtپیاده سازی پروژه API در دات نت ، قسمت پنجم ، عملیات CRUD در RestAPIپیاده سازی پروژه API در دات نت ، قسمت ششم ( قسمت آخر ) ، عملیات CRUD در RestAPI پارت دوم</description>
                <category>رضا منصوری</category>
                <author>رضا منصوری</author>
                <pubDate>Mon, 13 Jun 2022 20:13:26 +0430</pubDate>
            </item>
                    <item>
                <title>پیاده سازی پروژه API در دات نت ، قسمت پنجم ، عملیات CRUD در  RestAPI</title>
                <link>https://virgool.io/@reza.mansouri/%D9%BE%DB%8C%D8%A7%D8%AF%D9%87-%D8%B3%D8%A7%D8%B2%DB%8C-%D9%BE%D8%B1%D9%88%DA%98%D9%87-api-%D8%AF%D8%B1-%D8%AF%D8%A7%D8%AA-%D9%86%D8%AA-%D9%82%D8%B3%D9%85%D8%AA-%D9%BE%D9%86%D8%AC%D9%85-%D8%B9%D9%85%D9%84%DB%8C%D8%A7%D8%AA-crud-%D8%AF%D8%B1-restapi-h48kzfnquxhb</link>
                <description>سلام :)قسمت های قبلی رو اینجا میتونید ببینید.پیاده سازی پروژه API در دات نت ، قسمت اول ، معماری پیاز (Onion Architecture)پیاده سازی پروژه API در دات نت ، قسمت دوم ، ApplicationServices و DataLayerپیاده سازی پروژه API در دات نت ، قسمت سوم ، EndPoint توسط RestAPIپیاده سازی پروژه API در دات نت ، قسمت چهارم ، RestAPI Authentication Jwtالبته هنوز از کارمون مونده ولی سورس کامل این مقالات رو میتونید از اینجا ببینید.در پایان قسمت قبلی ، عملیات لاگین کاربرانمون با موفقیت انجام شد ، ادامه ی کار که اضافه کردن ، ویرایش و حذف موجودیت UserExam یا همون آزمونهای کاربرانمون هستش و نیاز به اعتبار سنجی کاربر هستش رو ادامه میدیم.برای شروع یک کنترلر جدید به نام ExamController در کنترلر های پروژه Api میسازیم ، سپس سرویس آزمون کاربران رو توسط اینترفیس IUserExamService داخل سازنده ی کنترلر تزریق میکنیم ، به این شکلExamController.cs    [Route(&amp;quotapi/[controller]&amp;quot)]
    [ApiController]
    public class ExamController : ControllerBase
    {
        private readonly IUserExamService _userexamService;
        public ExamController(IUserExamService UserexamService)
        {
            this._userexamService = UserexamService;
        }
    }سپس شروع به نوشتن اکشن های مد نظرمون میکنیم ، با ثبت آزمون شروع میکنیم بنابرین یک DTO برای انتقال اطلاعات آزمون برای ثبت آزمون جدید به نام AddUserExamDto در پروژه ی ApplicationServices ، پوشه ی Src  01.Core ApplicationServices Models UserExam  میسازیم. AddUserExamDtopublic class AddUserExamDto
    {
        public string Name { get; set; }
        public DateTime? StartDate { get; set; }
        public DateTime? EndDate { get; set; }
    }همچنین باید در سرویس UserExamService یک متد برای پیاده سازی عملیات اضافه کردن آزمون اضافه کنیم ، ابتدا در اینترفیس IUserExamService و سپس در خود سرویس UserExamService متد MakeExam را پیاده میکنیم . از آنجا که میدونیم که قراره سرویس ما چه عملیات هایی ( اضافه کردن آزمون ، ویرایش آزمون ، گرفتن آزمون های کاربر و در نهایت حذف آزمون  ) رو پیاده کنه تمام عملیات های مد نظرمون رو یکجا به اینترفیس و سرویس  UserExamService اضافه میکنیم .IUserExamService public interface IUserExamService
    {           
        DomainClass.UserExam.UserExam MakeExam(AddUserExamDto inputExam,string? currentUser);
        DomainClass.UserExam.UserExam Edit(EditUserExamDto inputExam, string? currentUser);
        List&lt;UserExamItem&gt; GetExam(int pageNumber , int pageSize, string? currentUser);
        bool Delete(long ID, string? currentUser);
    }و پیاده سازی اونها در سرویس UserExamService UserExamService  public class UserExamService : IUserExamService
    {
        private readonly IUserExamRepository _examRepository;
        private readonly IUserService _userService;
        public UserExamService(IUserExamRepository examRepository, IUserService userService)
        {
            _examRepository = examRepository;
            _userService = userService;
        }
        public DomainClass.UserExam.UserExam MakeExam(AddUserExamDto inputExam, string? currentUser)
        {
            DomainClass.UserExam.UserExam itemForAdd = new DomainClass.UserExam.UserExam()
            {
                Name = inputExam.Name,
                User = _userService.GetUserByMobile(currentUser),
                StartDate = inputExam.StartDate,
                EndDate = inputExam.EndDate,
                CreatedDate = DateTime.Now,
            };
            _examRepository.Insert(itemForAdd);
            _examRepository.SaveChanges();
            return itemForAdd;
        }
        public bool Delete(long ID, string? currentUser)
        {
            var exam = _examRepository.Get(Convert.ToInt32(ID));
            var user = _userService.GetUserByMobile(currentUser);
            if (exam.User.Id == user.Id)
            {
                exam.IsDeleted = true;
                _examRepository.Update(exam);
                _examRepository.SaveChanges();
            }
            else
            {
                throw new UnauthorizedAccessException();
            }
            return true;
        }
        public DomainClass.UserExam.UserExam Edit(EditUserExamDto inputExam, string? currentUser)
        {
            var exam = _examRepository.Get(inputExam.Id);
            var user = _userService.GetUserByMobile(currentUser);
            if (exam.User.Id == user.Id)
            {
                exam.Name = inputExam.Name;
                exam.EndDate = inputExam.EndDate;
                exam.StartDate = inputExam.StartDate;
                _examRepository.Update(exam);
                _examRepository.SaveChanges();
            }
            else
            {
                throw new UnauthorizedAccessException();
            }
            return exam;
        }
        public List&lt;UserExamItem&gt; GetExam(int pageNumber, int pageSize, string? currentUser)
        {
            var user = _userService.GetUserByMobile(currentUser);
            return _examRepository.GetQueryable().Where(u =&gt; u.User.Id.ToString().ToLower() == user.Id.ToString().ToLower()).Skip((pageNumber - 1) * pageSize).Take(pageSize).Select(o =&gt;                new UserExamItem
                {
                    Id = o.Id,
                    Name = o.Name,
                    StartDate = o.StartDate,
                    EndDate = o.EndDate
                }).ToList();
        }       
    }خب در UserExamService.cs ابتدا در سازنده ی سرویس ریپازیتوری IUserExamRepository  و سرویس IUserService رو تزریق کردیم ، در این سرویس ما نیاز به اطلاعات کاربر داریم و این اطلاعات توسط سرویس UserService و متد GetUserByMobile که قبلتر نوشتیم گرفته میشه ، IUserExamRepository  هم ریپازیتوری خود آزمون هامون هستش و برای ذخیره و ویرایش و بازیابی اطلاعات از دیتا بیسمون لازمش داریم.ابتدا متد MakeExam که برای ساخت آزمون هستش رو پیاده کردیم به این شکلUserExamService.cs public DomainClass.UserExam.UserExam MakeExam(AddUserExamDto inputExam, string? currentUser)
        {
            DomainClass.UserExam.UserExam itemForAdd = new DomainClass.UserExam.UserExam()
            {
                Name = inputExam.Name,
                User = _userService.GetUserByMobile(currentUser),
                StartDate = inputExam.StartDate,
                EndDate = inputExam.EndDate,
                CreatedDate = DateTime.Now,
            };
            _examRepository.Insert(itemForAdd);
            _examRepository.SaveChanges();
            return itemForAdd;
        }ورودیمون یک AddUserExamDto برای اطلاعات ثبت آزمون و همچنین شماره موبایل کاربری که آزمون رو ثبت میکنه به اسم متغیر currentUser هستش ، در ادامه ار دامین مدل UserExam یک شیئ ساختیم و اطلاعات اون رو از ورودیمون پر کردیم ، برای دریافت کد کاربر هم از سرویس User متد GetUserByMobile استفاده کردیم که موبایل رو میگیره و کاربر رو به ما میده ، در نهایت شیئ ایجاد شده رو توسط ریپازیتوریمون Insert و سپس در دیتا بیس ذخیره میکنیم ، در نهایت مدل ساخته شدمون رو به عنوان خروجی برمیگردونیم.در ادامه برای ویرایش آزمون متد Edit رو پیاده کردیمUserExamService.cs public DomainClass.UserExam.UserExam Edit(EditUserExamDto inputExam, string? currentUser)
        {
            var exam = _examRepository.Get(inputExam.Id);
            var user = _userService.GetUserByMobile(currentUser);
            if (exam.User.Id == user.Id)
            {
                exam.Name = inputExam.Name;
                exam.EndDate = inputExam.EndDate;
                exam.StartDate = inputExam.StartDate;
                _examRepository.Update(exam);
                _examRepository.SaveChanges();
            }
            else
            {
                throw new UnauthorizedAccessException();
            }
            return exam;
        }در ویرایش هم اطلاعات ورودیمون برای آزمون مدل EditUserExamDto و برای کاربر متغیر currentUser  هستش ، ابتدا آزمونی که قرار هستش ویرایش بشه رو از ریپازیتوری دریافت کردیم ، کاربر رو هم از سرویس UserService  گرفتیم ، سپس چک میکنیم که آیا آزمونی که میخواهیم ویرایش کنیم برای همین کاربر هست یا نه ، اگر نبود که خطای UnauthorizedAccessException به معنی عدم مجوز لازم برمیگردونیم و اگر بود مقادیر جدید رو از ورودی EditUserExamDto به آیتمی که ویرایش میکنیم نسبت میدیم و در آخر توسط ریپازیتوری ذخیره میکنیم و همون مدل ویرایش شده رو به عنوان خروجی برمیگردونیم.برای حذف هم از متد Delete استفاده کردیم و مثل همان متد Edit  هست با این تفاوت که کافی هستش مقدار IsDeleted موجودیت آزمون  رو True کنیم تا در واقع حذف منطقی یا همون SoftDelete انجام داده باشیم ، قبلتر در کانتکست تعریف کردیم آزمون هایی که مقدار IsDeleted  آنها True هستش رو در نظر نگیر و جوری رفتار میشه انگار در دیتا بیس وجود ندارند.modelBuilder.Entity&lt;UserExam&gt;().HasQueryFilter(c =&gt; c.IsDeleted == false);UserExamService.cspublic bool Delete(long ID, string? currentUser)
        {
            var exam = _examRepository.Get(Convert.ToInt32(ID));
            var user = _userService.GetUserByMobile(currentUser);
            if (exam.User.Id == user.Id)
            {
                exam.IsDeleted = true;
                _examRepository.Update(exam);
                _examRepository.SaveChanges();
            }
            else
            {
                throw new UnauthorizedAccessException();
            }
            return true;
        }میمونه متد GetExam که آزمون های ثبت شده توسط کاربر رو برمیگردونه به این شکلUserExamService.cs public List&lt;UserExamItem&gt; GetExam(int pageNumber, int pageSize, string? currentUser)
        {
            var user = _userService.GetUserByMobile(currentUser);
            return _examRepository.GetQueryable().Where(u =&gt; u.User.Id == user.Id).Skip((pageNumber - 1) * pageSize).Take(pageSize).Select(o =&gt;
                new UserExamItem
                {
                    Id = o.Id,
                    Name = o.Name,
                    StartDate = o.StartDate,
                    EndDate = o.EndDate
                }).ToList();
        }       ورودی اون شماره صفحه و تعداد آیتم ها در هر صفحه و کاربری که میخوایم آزمون ها ی مربوط بهش برگرده هستش و خروجی لیستی از مدل UserExamItem  که برای نمایش آزمون هاست.در این متد توسط GetQueryable در ریپازیتوریمون یک Where زدیم و آزمون ها رو به کاربر ورودیمون محدود کردیم ، سپس توسط Skip و Take منطق صفحه بندی رو پیاده کردیم و در آخر توسط Select به خروجی مورد نظرمون تبدیل کردیم.خب سرویسمون کامل شد ، میمونه استفاده از اون داخل کنترلر ExamController که قبلتر در سازنده تزریق کردیم استفاده کنیم.قبل از نوشتن اکشن هامون یک متد Private برای دریافت شماره موبایل کاربر جاری در کترلر  ExamController  مینویسیم تا هرجا شماره موبایل کاربر رو خواستیم ازش استفاده کنیم.getCurrentUserMobileprivate string? getCurrentUserMobile() 
        {
            if (HttpContext.User.Claims.Any())
                return HttpContext.User.Claims.First().Value;
            return null;
        }در این کد شماره موبایل کاربر در در اولین Claims ذخیره شده رو برمیگردونیم در هنگام دادن توکن اولین Claim که به توکن دادیم شماره موبایل بوده .بحث Claim ها از مقاله ی ما خارج هست اما به طور خلاصه یک ویژگی است که برای توصیف کردن یک موضوع استفاده میشود(از دید ما ،شناسایی هویت) و هنگام ساخت توکن در UserController اونها رو نسبت دادیم .UserControllervar claims = new[] {
                new Claim(JwtRegisteredClaimNames.NameId, mobile),
                new Claim(JwtRegisteredClaimNames.UniqueName, Guid.NewGuid().ToString())
            };خب نوشتن اکشن هامون رو شروع میکنیم اولین اکشن Post هستش برای ثبت آزمون جدید به این شکلExamController.cs        [HttpPost(&amp;quotAddExam&amp;quot)]
        [Authorize]
        public IActionResult Post([FromBody]AddUserExamDto addModel)
        {
            var result = _userexamService.MakeExam(addModel, getCurrentUserMobile());
            return StatusCode(201, result.Id);
        }نوع اکشن Post هست و فیلتر [Authorize] رو قبل از اکشن میزاریم تا لاگین بودن کاربر چک بشه ، اگر کاربر لاگین نباشه قبل از اجرای اکشن خودکار وضعیت 401 Unauthorized برگشت داده میشه. داخل اکشن هم ورودیمون از بادی درخواست یک AddUserExamDto هستش که همون رو به سرویس UserExamService میفرستیم و در نهایت وضعیت 201 Created به معنی ثبت ایتم جدید به همراه ID آیتم ثبت شده به خروجی اکشن میفرستیم.میریم که اکشن Post در ExamController رو تست کنیم   ، پروژه رو اجرا میکنیم و یو آی Swagger برامون بالا میاد ، میبینیم که یک آیتم Post برای کنترلر آزمون یا همون Exam اضافه شده .اکشن Post کنترلر Exam رو اجرا میکنیم ولی در خروجی میبینیم که به ما 401 Unauthorized برگردونند.دلیل برگشت وضعیت 401 Unauthorized بخاطر اینه که ما در اکشن از  فیلتر [Authorize] استفاده کردیم ولی چون توکن معتبر نفرستادیم ، اعتبار سنجی انجام نشد و 401 Unauthorized برگشت داده شد ، برای حل این مورد باید ابتدا عملیات Login رو انجام بدیم و توکن بگیریم و در درخواستمون Token رو در Header  ارسال کنیم بنابرین اکشن Login در کنترل User رو  توسط خود Swagger اجرا میکنیم ، نام کاربری و پسورد رو ارسال میکنیم و توکن میگیریم.خب حالا میتونیم بریم برای ثبت آزمون جدید ، اگر api ها ما برای دسترسی نیاز به مجوز های مانند توکن JWT داشته باشند می توانیم این قابلیت را  به Swagger اضافه کنیم که بتوانیم به همراه هر درخواست token را هم با header ارسال کنیم ، اما اینجا این کار را نمیکنیم و از Postman استفاده میکنیم تا یک ابزار دیگه رو هم ببینیم.در واقع Postman یک نرم افزار ( البته اکستنشن اون هم موجوده ) هستش که با اون میتویم RestApi خودمون رو تست کنیم.کار با Postman خیلی راحته و بنظرم نیاز به آموش خاصی نداره چون همه چی داخلش مشخصه ، Postman رو اجرا میکنیم و یک درخواست جدید میزنیم.آدرس درخواست رو که برای ثبت آزمون هستش رو وارد میکنیم https://localhost:7229/api/Exam/AddExamنوع درخواست هم Post هستش ، بادی درخواست رو که از نوع AddUserExamDto  هستش رو به این شکل پر میکنیم&amp;quotname&amp;quot: &amp;quotآزمون اول&amp;quot,
&amp;quotstartDate&amp;quot: &amp;quot2022-01-09T16:55:01.785Z&amp;quot,
&amp;quotendDate&amp;quot: &amp;quot2022-01-09T16:55:01.785Z&amp;quotنام آزمون ، تاریخ شروع و تاریخ پایان که همون مدل AddUserExamDto  هستش .فقط یک مورد میمونه اون هم ارسال توکن که قبلتر گرفتیم داخل درخواستمون هستش.به تب  Authorization میریم نوع رو Bearer Token میزاریم و توکن که در لاگین دریافت کردیم رو داخل Token میزاریم.حالا دکمه ی Send رو میزنیم تا نتیجه رو ببینیم تنیجه خروجی وضعیت 201 Created هستش به همراه مقدار 2 که آی دی آزمون جدیدی که ذخیره شده هستش که خودمون در اکشن برگردوندیم . return StatusCode(201, result.Id);دیتا بیس رو چک میکنیم و میبینیم که آزمون با ای دی 2 ثبت شدهخب تا اینجای کار سرویس UserExamService  رو کامل کردیم و از متد MakeExam در پروژه ی Api در کنترل ExamController استفاده کردیم و تست هم انجام دادیم و میتونیم آزمون های خودمون رو اضافه (Insert) کنیم.در مقاله ی بعدی اکشن های GetAll Update Delete  تا عملیات CRUD ما کامل بشه البته سرویس اونها رو داخل UserExamService.cs نوشتیم و فقط باید در کنترلر ExamController  اکشن های اونها رو کامل کنیم .البته هنوز از کارمون مونده ولی سورس کامل این مقالات رو میتونید از اینجا ببینید.امیدوارم از این مطلب خوشتون اومده باشه :)پیاده سازی پروژه API در دات نت ، قسمت اول ، معماری پیاز (Onion Architecture)پیاده سازی پروژه API در دات نت ، قسمت دوم ، ApplicationServices و DataLayerپیاده سازی پروژه API در دات نت ، قسمت سوم ، EndPoint توسط RestAPIپیاده سازی پروژه API در دات نت ، قسمت چهارم ، RestAPI Authentication Jwtپیاده سازی پروژه API در دات نت ، قسمت پنجم ، عملیات CRUD در RestAPIپیاده سازی پروژه API در دات نت ، قسمت ششم ( قسمت آخر ) ، عملیات CRUD در RestAPI پارت دوم</description>
                <category>رضا منصوری</category>
                <author>رضا منصوری</author>
                <pubDate>Sun, 12 Jun 2022 23:27:53 +0430</pubDate>
            </item>
                    <item>
                <title>پیاده سازی پروژه API در دات نت ، قسمت چهارم ،  RestAPI Authentication Jwt</title>
                <link>https://virgool.io/@reza.mansouri/%D9%BE%DB%8C%D8%A7%D8%AF%D9%87-%D8%B3%D8%A7%D8%B2%DB%8C-%D9%BE%D8%B1%D9%88%DA%98%D9%87-api-%D8%AF%D8%B1-%D8%AF%D8%A7%D8%AA-%D9%86%D8%AA-%D9%82%D8%B3%D9%85%D8%AA-%DA%86%D9%87%D8%A7%D8%B1%D9%85-restapi-authentication-jwt-hday2s6owwmw</link>
                <description>سلام :)قسمت های قبلی رو اینجا میتونید ببینید.پیاده سازی پروژه API در دات نت ، قسمت اول ، معماری پیاز (Onion Architecture)پیاده سازی پروژه API در دات نت ، قسمت دوم ، ApplicationServices و DataLayerپیاده سازی پروژه API در دات نت ، قسمت سوم ، EndPoint توسط RestAPIدر پایان قسمت قبلی ، عملیات ثبت نام کاربرانمون با موفقیت انجام شد ، در ادامه ی کار قسمت لاگین برنامه رو کار میکنیم.برای لاگین در Api ما از JWT برای اعتبار سنجی کاربر هامون استفاده میکنیم ،در نظر داشته باشید که مبحث JWT مبحث بزرگیه و برای توضیح کامل نیاز به چندین مقاله هستش که از بحث ما خارجه و فقط استفاده ی اون رو توضیح میدیم.خب JWT چجوری کار میکنه؟ به طور خلاصه به این شکل که کاربر که قبلتر عضو شده درخواست لاگین میده بعد از چک کردن نام کاربری و پسورد ، ما به کاربر یک توکن برمیگردونیم (توکن رشته ای از کاراکتر هاست که توسط اون اعتبار سنجی انجام میشه) ، سپس در هر درخواستی که نیاز به اعتبار سنجی داره، توکن رو ارسال میکنیم و  پس از معتبر بودن توکن کار ادامه پیدا میکنه.برای کار با JWT باید پکیج زیر رو در پروژه ی API مون نصب کنیم.Microsoft.AspNetCore.Authentication.JwtBearerبعد از نصب یکسری تنظیمات باید انجام بدیم ، کد های زیر رو به فایل Program.cs اضافه میکنیمProgram.csbuilder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
            .AddJwtBearer(options =&gt;
            {
                options.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuer = true,
                    ValidateAudience = true,
                    ValidateLifetime = true,
                    ValidateIssuerSigningKey = true,
                    ValidIssuer = builder.Configuration[&amp;quotJwt:Issuer&amp;quot],
                    ValidAudience = builder.Configuration[&amp;quotJwt:Issuer&amp;quot],
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration[&amp;quotJwt:Key&amp;quot]))
                };
            });در این تکه کد ما توسط AddAuthentication سرویس JWT رو به برنامه مون اضافه میکنیم ، که یکسری Option یا تنظیمات هم می‌گیریه ، ValidateIssuer  ValidateAudience ValidateLifetime  ValidateIssuerSigningKey  رو مقدار true بهشون میدیم  ، و مقدار ValidIssuer  ValidAudience IssuerSigningKey رو از تنظیمات میخونیم ، تنظیماتی که در فایل appsettings.json اضافه کردیم.appsettings.json  &amp;quotJwt&amp;quot: {
    &amp;quotKey&amp;quot: &amp;quotThisismySecretKey&amp;quot,
    &amp;quotIssuer&amp;quot: &amp;quotTest.com&amp;quot
  }
}در اینجا نمیخوایم وارد به جزئیات استفاده ار JWT وارد شیم ، تنظیماتی که مقدار true رو دادیم سختگیری بیشتری در چک کردن توکن انجام میده و تظیمات Key همون کلید مورد استفاده ما و Issuer هم به عنوان صادر کننده هستش ،   برای تنظیم Key یا کلیدمون میتونیم از یک عبارت پسورد مانند استفاده کنیم و برای Issuer هم از نام سایت یا اپلیکیشن.نکته ی دیگری که هستش در حالت پیشفرض برنامه های Asp Core Web Api در فایل Program.cs میان افزار یا Middleware  app.UseAuthorization ، خودش به شکل پیشفرض وجود داره ولی برای استفاده از Authentication باید میان افزار ( Middleware )  UseAuthentication رو قبل از UseAuthorization اضافه کنیم.اینجا یک سوال پیش میاد که تفاوت  Authentication و Authorization چیه . شناسايی هويت یا Authentication  برای تشخیص کاربر (معتبر بودن نام کاربری، رمز عبور) به کار می رود و  Authorization برای تشخیص سطح دسترسی کاربر به اطلاعات می باشد که به منظور جلوگيری از دسترسی افراد غير مجاز به منابع و داده های حفاظت شده می‌باشد.وظیفه Authentication پاسخ به اين سوال است كه كاربر كيست و آيا واقعا همان كسی است كه ادعا می كند؟ ولي Authorization  مجموعه مجوز هايی است كه به يک كاربر تشخيص هويت داده شده داده می‌شود و وظيفه اش پاسخ به اين سوال است كه يک كاربر مجاز ( تشخيص هويت داده شده ) چه كار می‌تواند بكند و به چه اطلاعاتی می‌تواند دسترسی داشته باشد.به این شکلapp.UseAuthentication();
app.UseAuthorization();در نهایت فایل Program.cs ما به این شکل میشودProgram.csbuilder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
            .AddJwtBearer(options =&gt;
            {
                options.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuer = true,
                    ValidateAudience = true,
                    ValidateLifetime = true,
                    ValidateIssuerSigningKey = true,
                    ValidIssuer = builder.Configuration[&amp;quotJwt:Issuer&amp;quot],
                    ValidAudience = builder.Configuration[&amp;quotJwt:Issuer&amp;quot],
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration[&amp;quotJwt:Key&amp;quot]))
                };
            });;
builder.Services.AddScoped&lt;IUserService, UserService&gt;();
builder.Services.AddScoped&lt;IUserRepository, UserRepository&gt;();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();در ادامه میریم برای نوشتن اکشن Login در کنترلر Userابتدا باید یک DTO برای انتقال اطلاعات مربوط به لاگین ایجاد کنیم بنابرین کلاس LoginUserDto رو در داخل پوشه ی Src 01.Core پروژه ه ی ApplicationServices و پوشه ی Models User میسازیمLoginUserDto public class LoginUserDto
    {
        public string Mobile { get; set; }
        public string Password { get; set; }
    }خب پراپرتی هایی که داریم نام کاربر و پسورد برای عملیات ورود کاربر هستش ، برای اعتبار سنجی مدلمون هم  مثل مقاله ی قبلی که  فایل AddUserDtoValidation.cs رو برای اعتبار سنجی ثبت کاربرمون ساختیم ،فایل LoginUserDtoValidation رو برای اعتبار سنجی لاگین میسازیم و Rule ها مون رو تعریف میکنیم.LoginUserDtoValidation.cs  public class LoginUserDtoValidation : AbstractValidator&lt;LoginUserDto&gt;
    {
        public LoginUserDtoValidation()
        {
            RuleFor(c =&gt; c.Mobile).NotEmpty().WithMessage(&amp;quotبرای موبایل مقدار لازم است&amp;quot)
                .Length(11).WithMessage(&amp;quotموبایل 11 رقم می‌باشد&amp;quot);
            RuleFor(c =&gt; c.Password).NotEmpty().WithMessage(&amp;quotبرای پسورد مقدار لازم است&amp;quot);
        }
    }وارد جزئیات تعریف این کلاس نمیشیم چون در مقاله ی قبلی در ساخت AddUserDtoValidation  توضیح دادیم ، در اینجا دو قانون اضافه میکنیم ، یکی برای مقدار داشتن و طول رشته ی نام کاربری ( که همون موبایل هستش ) و یکی هم برای اجباری بودن پسورد.همچنین باید یادمون باشه که این کلاس رو به کلاسی که میخواهیم اعتبار سنجی بشه در فایل Program.cs نسبت بدیم.builder.Services.AddTransient&lt;IValidator&lt;LoginUserDto&gt;, LoginUserDtoValidation&gt;(); خب نوبت به نوشتن عملیات برسی اطلاعات کاربر میرسه ، برای این کار یک متد در اینترفیس IUserService  به نام CanLogin و پیاده سازی آن را در سرویس User کلاس UserService اضافه میکنیم .IUserService.cs public interface IUserService
    {
        bool CanLogin(LoginUserDto loginModel);
    }UserService.cs  public bool CanLogin(LoginUserDto loginModel)
        {
            var user = _userRepository.GetByMobile(loginModel.Mobile);
            if (user != null) 
            {
                return Security.SecurePasswordHasher.Verify(loginModel.Password,user.Password);
            }
            return false;
        }در اینجا ورودی ما همان LoginUserDto هستش و خروجی یک Boolean که نشان میدهید اطلاعات ورود صحیح است یا نه ، نکته ای که هستش اینه که ما باید توسط ریپازیتوری User ، کاربر رو توسط شماره موبایل که همون نام کاربری ما هستش دریافت کنیم ، سپس اگر کاربر وجود داشت باید پسورد وارد شده رو با پسورد کاربر رو توسط همون کلاس  Security که قبلتر اضافه کردیم  چک کنیم که یکی باشد ، در این صورت نام کاربری و پسورد درست هستش و مقدار True برمیگردونیم و در غیر این صورت False.یه مورد دیگه اینه که ما متد GetByMobile رو برای ریپازیتوری User نداریم و باید اضافه کنیم ، همونطور که قبلتر هم گفتیم ممکن هستش ما نیاز به اضافه کردن متد به ریپازیتوریمون داشته باشیم چون متد های پیشفرضی که در IRepository.cs ارث بری و پیاده کردیم جوابگوی کار ما نباشه ، مثل حالت فعلی که نیاز داریم موجودیت کاربر رو بر اساس موبایل بگیریم ، بنابرین میریم تا متد GetByMobile  رو به ریپازیتوری User اضافه کنیم.ابتدا متد رو به اینترفیس ریپازیتوری user اضافه میکنیمIUserRepository.cspublic interface IUserRepository : IRepository&lt;User&gt;
    {
        User GetByMobile(string mobile);
    }سپس پیاده سازی اون رو داخل UserRepository.cspublic class UserRepository : EfRepository&lt;User&gt;, IUserRepository
    {
        ApplicationContext _dbContext;
        public UserRepository(ApplicationContext DbContext) : base(DbContext)
        {
            _dbContext = DbContext;
        }
        public User GetByMobile(string mobile)
        {
            return _dbContext.Users.SingleOrDefault(c =&gt; c.Mobile == mobile);
        }
    }در اینجا توسط کانتکست ار دیتا بیس کاربری که شماره موبایل مورد نظرمون رو داره برمیگردونیم.خب اینجا متد CanLogin سرویس User کامل میشه و میتونیم داخل کنترلر User ازش استفاده کنیم ، به این شکلUserController.cs        [HttpPost(&amp;quotLogin&amp;quot)]
        public IActionResult Login([FromBody] LoginUserDto login)
        {
            if (_userService.CanLogin(login))
            {
                var tokenString = GenerateJSONWebToken(login);
                return Ok(new { token = tokenString });
            }
            else
                return StatusCode(401);
        }در اکشن Login چک میکنیم که اطلاعات ورود کاربر درست هستش یا نه اگر درست بود که بهش توکن رو برمیگردونیم تا از اون برای درخواستهای بعدیش استفاده کنه اگر نه هم وضعیت StatusCode(401) یا همون 401 Unauthorized رو برمیگردونیم که به معنی عدم احراز هویت هستش.در اینجا GenerateJSONWebToken برای ما توکن رو میسازه که به شکل زیر هستشGenerateJSONWebToken  private string GenerateJSONWebToken(string mobile)
        {
            var securityKey = new
SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config[&amp;quotJwt:Key&amp;quot]));
            var credentials = new SigningCredentials(securityKey,
SecurityAlgorithms.HmacSha256);
            var claims = new[] {
                new Claim(JwtRegisteredClaimNames.NameId, mobile),
                new Claim(JwtRegisteredClaimNames.UniqueName, Guid.NewGuid().ToString())
            };
            var token = new JwtSecurityToken(_config[&amp;quotJwt:Issuer&amp;quot],
                _config[&amp;quotJwt:Issuer&amp;quot],
                claims,
                expires: DateTime.Now.AddMinutes(60),
                signingCredentials: credentials);
            return new JwtSecurityTokenHandler().WriteToken(token);
        }توسط GenerateJSONWebToken  توکن ما ساخته میشه و به کاربر به عنوان خروجی ارسال میشه ، نکته ای که اینجا وجود داره ما تنظیم مقدار زمانی که توکن معتبر هست رو داریم و به اون مقداره یک ساعت یا شصت دقیقه رو دادیم(   expires: DateTime.Now.AddMinutes(60) ) که بسته به نیاز قابل تغییر هستش ، البته تکنیکی به نام RefreshToken نیز وجود داره که باعث میشه کمتر نیاز به انجام لاگین توسط کاربر باشه .خب عملیات لاگین ما هم کامل شد و میتونیم تست کنیم برای این کار پروژه رو اجرا میکنیم تا یو آی Swagger برای  ما بالا بیاد ، در اینجا میبینیم که یک آیتم دیگه برای در قسمت User اضافه شده به نام Login  که همون نام اکشن ما در کنترلر User هستش  برای ورود هم میتونیم دوباره ثبت نام دیگه ای کنیم و یا شماره موبایل و پسورد کاربری که قبلا ثبت نام کردیم رو وارد کنیم ، سپس دکمه ی Try it out رو میزنیمدر نتیجه میبینیم که به ما توکن رو برگشت داد که به شکل زیر هستش&amp;quottoken&amp;quot: &amp;quoteyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1laWQiOiIwOTEyMTQ3ODUyNSIsInVuaXF1ZV9uYW1lIjoiZGRhNjM0NDMtMGQ4Zi00NjQ5LTgyNTAtMmQxNDUzODQxYzIwIiwiZXhwIjoxNjU0OTY2NjIzLCJpc3MiOiJUZXN0LmNvbSIsImF1ZCI6IlRlc3QuY29tIn0.2NQl2_pFrXtL72QJFGOzV3M6_1ajS-FT93LuOiHjjQ8&amp;quotدر برنامه توسط این توکن اعتبار سنجی انجام میشه و میتونیم عملیات هایی که نیاز به اعتبار سنجی داره رو توسط این توکن که برای کاربری که لاگین کردیم رو انجام بدیم.ادامه ی کار که اضافه کردن ، ویرایش و حذف موجودیت UserExam یا همون آزمونهای کاربرانمون هستش و نیاز به اعتبار سنجی کاربر رو داره در مقالات بعدی انجام میدیم.البته هنوز از کارمون مونده ولی سورس کامل این مقالات رو میتونید از اینجا ببینید.امیدوارم از این مطلب خوشتون اومده باشه :)پیاده سازی پروژه API در دات نت ، قسمت اول ، معماری پیاز (Onion Architecture)پیاده سازی پروژه API در دات نت ، قسمت دوم ، ApplicationServices و DataLayerپیاده سازی پروژه API در دات نت ، قسمت سوم ، EndPoint توسط RestAPIپیاده سازی پروژه API در دات نت ، قسمت چهارم ، RestAPI Authentication Jwtپیاده سازی پروژه API در دات نت ، قسمت پنجم ، عملیات CRUD در RestAPIپیاده سازی پروژه API در دات نت ، قسمت ششم ( قسمت آخر ) ، عملیات CRUD در RestAPI پارت دوم</description>
                <category>رضا منصوری</category>
                <author>رضا منصوری</author>
                <pubDate>Sat, 11 Jun 2022 22:07:44 +0430</pubDate>
            </item>
                    <item>
                <title>پیاده سازی پروژه API در دات نت ، قسمت سوم ، EndPoint توسط RestAPI</title>
                <link>https://virgool.io/@reza.mansouri/%D9%BE%DB%8C%D8%A7%D8%AF%D9%87-%D8%B3%D8%A7%D8%B2%DB%8C-%D9%BE%D8%B1%D9%88%DA%98%D9%87-api-%D8%AF%D8%B1-%D8%AF%D8%A7%D8%AA-%D9%86%D8%AA-%D9%82%D8%B3%D9%85%D8%AA-%D8%B3%D9%88%D9%85-endpoint-%D8%AA%D9%88%D8%B3%D8%B7-restapi-m3aeltxkr429</link>
                <description>سلام :)خب در دو قسمت قبل تعریف ساختار و معماری پروژه ما انجام شد .پیاده سازی پروژه API در دات نت ، قسمت اول ، معماری پیاز (Onion Architecture)پیاده سازی پروژه API در دات نت ، قسمت دوم ، ApplicationServices و DataLayer در این قسمت میرسیم به قسمت EndPoint یا همون Presentation .در این پروژه همونطور که قبلتر هم گفتیم میخوایم یک وب سرویس Rest بسازیم .پروژه ای از نوع WebApi رو داخل پوشه ی  Endpoint , Src میسازیم .  موقع ساختن پروژه حتما تیک Enable OpenApi Support رو میزنیم تا برای ما Swagger اضافه بشه.البته بعدا هم میتونیم داخل پروژه پکیج های مورد نیاز برای Swagger رو اضافه کنیم ولی هنگام ساخت پروژه اگر این تیک رو بزنیم خودکار هنگام ساخت پروژه Swagger  اضافه میشه.خب ممکنه سوال پیش بیاد که چیه این Swagger  در حقیقت Swagger یک زبان توصیف رابط ( Interface Description Language ) هستش که توسط یک فایل JSON یا YAML طراحی و نوشته میشه یعنی به طور ساده یک واسط بین Server و Client هستش که ارتباطات بین این دو را برای شما راحت میکنه مثلا در یک تیم برنامه نویسی یکی از مشکلات کسایی که بخش بک اند رو توسعه میدن توضیح دادن APIها به فرانت اند کارهاست ? با swagger می‌تونید بیشتر زمان و تمرکزتون رو بزارید رو بیزینس لاجیکتون چون یک Documentation خیلی کامل برای شما به صورت کاملا اتوماتیک ایجاد می‌کنه ( در اکثر زبان های برنامه نویسی ) و شما نیازی نیست وقتتون رو صرف نوشتن داکیومنت کنید.این ابزار کمک زیادی برای تست عملکرد پروژه میکنه که جلو تر میبینیم.بعد از ساخت پروژه چنین فایل هایی رو داریم.فایل WeatherForecastController و WeatherForecast رو فایل نمونه پروژه اولیه هستند رو پاک میکنیم.برای شروع کار ابتدا یک کنترلر به نام UserController ایجاد میکنیم تا عملیات مورد نیاز مربوط به انتیتی User رو داخل همین کنترلر انجام بدیم.اولین کاری که میخوایم انجام بدیم ثبت نام کاربر هستش بنابرین یک DTO برای گرفتن اطلاعات کاربر به نام AddUserDto داخل پوشه ی Src  01.Core  پروژه ه ی ApplicationServices و پوشه ی Models User  میسازیم تا اطلاعات ورودی برای ثبت نام رو بگیریم .کلاس های DTO برای انتقال دیتا هستند.در مورد کلاس های DTO اینجا میتونید بیشتر بخونید.در نظر داشته باشید که قبلتر هم گفتیم که مدل های برناممون رو داخل پوشه Models در پروژه ی ApplicationServices  قرار میدیم.public class AddUserDto
    {
        public string Mobile { get; set; }
        public string Name { get; set; }
        public string Password { get; set; 
    } شاید این سوال پیش بیاد که چرا مدل هامون رو داخل همین پروژه ِ EndPint نمیزاریم؟فرض کنید ما یک EndPoint نداریم یک پروژه ی Api داریم و یک پروژه ی وب سایت یا حتی بیشتر ، باید برای هرکدوم مدل های جدا تعریف کنیم؟ یا به هم رفرنس بدیم و از مدل های هم دیگه استفاده کنیم!هیچکدوم ، روش درست در معماری Onion Architecture اینه که مدل های ما جزو بیزینس ما هستش و باید یکجا داخل پروژه ی ApplicationServices ما تعریف بشه و هر قسمتی که به این مدل ها نیاز داره از همین لایه ی ApplicationServices مدل ها رو بخونه.خب کلاس AddUserDto  دارای سه پراپرتی هستش برای ثبت نام کاربر که از اسم هاشون هم مشخصه ، موبایل ، نام کاربر و پسورد برای لاگین .علاوه بر این نیاز داریم تا برای سرویس User یک متد تعریف کنیم که عملیات ثبت نام کاربر رو انجام بده ، پس اولین متد ما برای سرویس User میشه متد Register .سراغ سرویس User داخل   Core  ApplicationServices پو شه ی Services و User میریم ، ابتدا در کلاس اینترفیس IUserService متد Register که ورودی اون AddUserDto  هستش و خروجیش خود انتیتی User رو  تعریف میکنیم سپس پیاده سازیش رو داخل خود کلاس UserService .IUserService.cs public interface IUserService
    {
        User Register(AddUserDto addUserModel);
    }UserService.cspublic class UserService : IUserService
    {
        private readonly IUserRepository _userRepository;
        public UserService(IUserRepository userRepository)
        {
            _userRepository = userRepository;
        }
        public User Register(AddUserDto addUserModel)
        {
            var hashpassword = Security.SecurePasswordHasher.Hash(addUserModel.Password);
            var user = new User()
            {
                Mobile = addUserModel.Mobile,
                Name = addUserModel.Name,
                Password = hashpassword
            };
            _userRepository.Insert(user);
            _userRepository.SaveChanges();
            return user;
        }
}خب اینجا یکسری موارد هست که باید توضیح داده بشه.ابتدا اینکه ما داخل سرویس هامون  ریپازیتوریی که قبلتر برای همون موجودیت رو ساختیم رو به کلاس تزریق میکنیم که در این مثال IUserRepository  رو که برای ریپازیتوری User هستش رو تزریق کردیم .private readonly IUserRepository _userRepository; 
public UserService(IUserRepository userRepository)         
{
             _userRepository = userRepository;         
}سپس متد Register رو کامل میکنیم public User Register(AddUserDto addUserModel)
        {
            var hashpassword = Security.SecurePasswordHasher.Hash(addUserModel.Password);
            var user = new User()
            {
                Mobile = addUserModel.Mobile,
                Name = addUserModel.Name,
                Password = hashpassword
            };
            _userRepository.Insert(user);
            _userRepository.SaveChanges();
            return user;
        }در این متد ایتدا پسوردی که  ورودی هستش رو هش میکنیم ، همونطور که میدونید پسورد ها در دیتابیس باید بشکل هش شده ذخیره بشن ، برای این کار از یک کلاس دیگه استفاده کردیم که کارش عملیات مربوط به هش کردن و مقایسه ی پسورد هستش که وارد جزییاتش نمیشیم.در ادامه از دامین کلاس User یک شیئ ایجاد میکنیم و از با اطلاعات DTO وردودیمون پرش میکنیم ، سپس به متد Insert ریپازیتوریمون پاس میدیم و در در آخر متد SaveChanges رو کال کرده تا در دیتا بیس ذخیره بشه و همچنین خود User که ساختیم رو به عنوان خروجی برمیگردونیم.متد Register  سرویس User کامل شد و میتونیم ازش استفاده کنیم ، برای همینکار داخل کنترلر UserController یک اکشن برای ثبت نام کاربر میسازیم به شکل زیر.    [Route(&amp;quotapi/User/&amp;quot)]
    [ApiController]
    public class UserController : ControllerBase
    {
        private readonly IUserService _userService;
        public UserController(IUserService userService)
        {
            _userService = userService;
        }
        [HttpPost(&amp;quotRegister&amp;quot)]
        public IActionResult Register([FromBody] AddUserDto addUser)
        {
            var user = _userService.Register(addUser);
            if (user != null)
                return StatusCode(201);
            return StatusCode(500);
        }
    }در ابتدا سرویس IUserService  رو داخل سازنده تزریق کردیم تا ازش استفاده کنیم .ما قرار هستش که کاربر رو ثبت کنیم پس اکشن ما از نوع HttpPost هستش و ورودی اون یک AddUserDto . در ادامه داخل اکشن متد Register  رو کال میکنیم و وردودیمون رو بهش پاس میدیم ، در  آخر چک میکنیم اگر خروجی متد نال نبود پس یوزر ساخته شده و  Status Code 201 که منظور وضعیت Created یا همون ساختن جدید رو برمیگردونیم ، در غیر این صورت وضعیت با کد 500 که خطای سرور هستش رو برگشت میدیم. برای اجرای این قسمت یکسری کارها مونده ، مثل تنظیمات اتصال به دیتا بیس و همچنین تزریق وابستگی هامون برای سرویس و ریپازیتوریمون داخل پروژه ی Api در فایل Program.cs .سراغ فایل Program.cs میریم تا موارد گفته شده رو انجام بدیم.یکسری کد ها بصورت ییشفرض داخل کلاس Program.cs هستند که فعلا با اونها کاری نداریمبرای تنظیم کانکشن استرینگ به دیتا بیسمون اون رو تو فایل appsettings.json تعریف کرده و سپس کانتکسمون رو اضافه میکنیمappsettings.json &amp;quotConnectionStrings&amp;quot: {
    &amp;quotAppCnn&amp;quot: &amp;quotServer=.; Initial Catalog=SampleApi; Integrated Security=True&amp;quot
  }Program.csvar cnnString = builder.Configuration.GetConnectionString(&amp;quotAppCnn&amp;quot);
builder.Services.AddDbContext&lt;ApplicationContext&gt;(options =&gt; options.UseSqlServer(cnnString));سپس توسط خود IOC Container خود دات نت کور سرویس و ریپازیتوریمون رو هم اضافه میکنیمbuilder.Services.AddScoped&lt;IUserService, UserService&gt;();
builder.Services.AddScoped&lt;IUserRepository, UserRepository&gt;();بحث تزریق وابستگی خودش یک یا چند مقاله ی جداست و مبحث بزرگیه ولی به طور خلاصه ما سه حالت برای این کار توسط خود دات نت کور داریم که بشکل زیر هستش1- Transient به ازای هر شی یک نمونه ساخته می شود.2- Scoped  به ازای هر درخواست سرور یک محدوده ساخته می شود. 3- Singleton  یک نمونه فقط ساخته می شود و در تمام طول عمر نرم افزار استفاده می شود.ما در این مثال سرویس ها و ریپازیتوری هامون رو توسط AddScoped انجام میدیم.در نهایت فایل Program.cs به این شکل میشهProgram.csvar builder = WebApplication.CreateBuilder(args);
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var cnnString = builder.Configuration.GetConnectionString(&amp;quotAppCnn&amp;quot);
builder.Services.AddDbContext&lt;ApplicationContext&gt;(options =&gt; options.UseSqlServer(cnnString));
builder.Services.AddScoped&lt;IUserService, UserService&gt;();
builder.Services.AddScoped&lt;IUserRepository, UserRepository&gt;();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();خب قبل از اینکه پروژه رو اجرا کنیم یکبار ارتباط با دیتا بیسمون رو تست میکنیم و اون رو میسازیم.برای این کار ایتدا دستور  add-migration init رو داخل Package manager Console میزنیم.دقت کنید که اجرای این دستور باید در پروژه ی DataLayer ما که همان پروژه ی DataLayer.SqlServer هستش باید باشه ، مانند تصویر زیربعد از انجام یک پوشه به نام Migrations به پروژه ی DataLayer.SqlServer  ما اضافه میشه که جزییات عملیات هستش.توضیح بیشتر در مورد Migration خارج از مقاله ی ما هستش ولی بطور خلاصه در پوشه Migration  کلاس هایی که ساخته میشه شامل جرئیات عملیات Migration هستش .نوبت به دستور update-database میرسه که برای ما دیتا بیس و جداول رو از روی مدل هامون میسازه ، دستور رو داخل Package manager Console اجرا میکنیم و میبینیم که دیتا بیس ما بر اساس دامین مدل هامون ساخته شدهخب میریم که پروژه رو اجرا کنیم و از ثبت نامی که نوشتیم تست بگیریم.پروژه که اجرا میشه همونطور که توی فایل Program.cs هم تعریف شده ، در محیط Development هستیم و یو آی Swagger برای ما بالا میاد و میتونیم اکشن هایی که داریم رو ببینیم. در اینجا فقط یک اکشن Register که از نوع Post هستش رو میبینیم.برای تست میتونیم دکمه ی Try it out رو بزنیم و مدل ورودیمون رو بفرستیم تا نتیجه رو ببینیم.میبینیم که در خروجی کد 201 برگشت داده میشه و عملیات با موفقیت انجام شده.در دیتا بیس هم رکورد ما ایجاد شده.در این قسمت یک مورد مونده اون هم اعتبار سنجی ورودی هامون هستش ، چون پروژه ما از نوع API هستش از FluentValidation برای اعتبار سنجی ورودیمون استفاده میکنیم.لازمه که ابتدا پکیج هایFluentValidationFluentValidation.AspNetCoreFluentValidation.DependencyInjectionExtensionsرو نصب کنیم سپس یک مدل داخل  Core  ApplicationServices Models User  به نام AddUserDtoValidation میسازیم ، به این شکلAddUserDtoValidation.cspublic class AddUserDtoValidation : AbstractValidator&lt;AddUserDto&gt;
    {
        public AddUserDtoValidation()
        {
            RuleFor(c =&gt; c.Mobile).NotEmpty().WithMessage(&amp;quotبرای موبایل مقدار لازم است&amp;quot)
                .Length(11).WithMessage(&amp;quotموبایل 11 رقم می‌باشد&amp;quot);
            RuleFor(c =&gt; c.Password).NotEmpty().WithMessage(&amp;quotبرای پسورد مقدار لازم است&amp;quot);
        }
    }همونطور که میبینید برای ساختن مدل اعتبار سنجی یک کلاس ساخته از AbstractValidator که حالت جنریک داره و AddUserDto رو بهش دادیم ، ارث بری میکنیم . سپس یک متد سازنده میسازیم و Rule ها مون رو تعریف میکنیم .در این مثال خالی نبودن موبایل و پسورد و مقدار طول موبایل رو اضافه کردیم.برای اینکه بشکل خودکار در API ورودی هامون اعتبار سنجی بشن باید یکسری تغییرات داخل فایل Program.cs مریوط به پروژه ی API مون بدیم ، به این شکل که کد های زیر رو داخل فایل Program.cs اضافه میکنیم.Program.csbuilder.Services.AddControllers().AddFluentValidation();
builder.Services.AddTransient&lt;IValidator&lt;AddUserDto&gt;, AddUserDtoValidation&gt;();در خط اول Fluent Validation رو در کنترلر ها فعال کردیم و در خط دوم کلاس ولیدیشنی که تعریف کردیم به خود کلاس AddUserDto به صورت Transient نسبت دادیم.با اینکار بشکل خودکار قبل از اجرای اکشن Register مدل AddUserDto  توسط ولیدیشنی که بهش نسبت دادیم چک میشه . به عنوان مثال شماره موبایل رو به جای 11 رقم 12 رقم میفرستیم و خروجی به شکل زیر میشه.میبینیم که اعتبار سنجی که تعریف کردیم درست کار میکنه :)در این قسمت پروژه ی لایه ی Endpiont یا همون Peresention رو از نوع RestApi ساختیم ، سپس در سرویس User که در لایه ی AplicationService ما هستش  عملیات Register رو توسط EF و SqlServer در لایه ی DataLayer انجام دادیم ، در حین کار با ابزار Swagger  و EfCore و FluentValidation  نیز استفاده کردیم  .در ادامه به سرویس User عملیات لاگین رو اضافه میکنیم و توسط JWT اعتبار سنجی مون رو با دادن توکن به کاربر ادامه میدیم , و با همین روند سرویس هامون رو کاملتر میکنیم.البته هنوز از کارمون مونده ولی سورس کامل این مقالات رو میتونید از اینجا ببینید.امیدوارم از این مطلب خوشتون اومده باشه :)پیاده سازی پروژه API در دات نت ، قسمت اول ، معماری پیاز (Onion Architecture)پیاده سازی پروژه API در دات نت ، قسمت دوم ، ApplicationServices و DataLayerپیاده سازی پروژه API در دات نت ، قسمت سوم ، EndPoint توسط RestAPIپیاده سازی پروژه API در دات نت ، قسمت چهارم ، RestAPI Authentication Jwtپیاده سازی پروژه API در دات نت ، قسمت پنجم ، عملیات CRUD در RestAPIپیاده سازی پروژه API در دات نت ، قسمت ششم ( قسمت آخر ) ، عملیات CRUD در RestAPI پارت دوم</description>
                <category>رضا منصوری</category>
                <author>رضا منصوری</author>
                <pubDate>Thu, 09 Jun 2022 13:30:02 +0430</pubDate>
            </item>
                    <item>
                <title>پیاده سازی پروژه API در دات نت ، قسمت دوم ، ApplicationServices و DataLayer</title>
                <link>https://virgool.io/@reza.mansouri/%D9%BE%DB%8C%D8%A7%D8%AF%D9%87-%D8%B3%D8%A7%D8%B2%DB%8C-%D9%BE%D8%B1%D9%88%DA%98%D9%87-api-%D8%AF%D8%B1-%D8%AF%D8%A7%D8%AA-%D9%86%D8%AA-%D9%82%D8%B3%D9%85%D8%AA-%D8%AF%D9%88%D9%85-applicationservices-%D9%88-datalayer-nxp3xzl5xdt8</link>
                <description>سلام :) در ادامه ی قسمت قبلپیاده سازی پروژه API در دات نت ، قسمت اول ، معماری پیاز (Onion Architecture) کارمون رو با توسعه ی قسمت ApplicationServices ادامه میدیم .در ابتدا باید بدونیم این لایه قراره چه کاری انجام بده لایه اپلیکیشن در بر گیرنده عملیاتی است که قوانین بیزینس ایجاب میکند که روی انتیتی های سیستم صورت بگیره. به عنوان مثال عملیات ذخیره اطلاعات مشتری ، خوندن اطلاعات و یا هر عملیاتی که در بیزینس ما تعریف شده .در نظر داشته باشید که ذخیره اطلاعات عملیاتی است که با مکانیزم های ذخیره و بازیابی اطلاعات مرتبط است و  تو لایه ی DataLayer در قسمت  02.Infrastructures  پیاده سازی میشن. بنابر این دیتابیس و جزییات نحوه ذخیره اطلاعات باید از قوانین بیزینس سیستم جدا باشند و این کار توسط پروژه ی  DataLayer.SqlServer  داخل پوشه ی 02.Infrastructures انجام میشه  .میریم برای پیاده سازی قسمت ApplicationServices کارمون رو رو شروع میکنیم ، ابتدا داخل پروژه ی ApplicationServices که داخل پوشه ی  Src و 01.Core هستش ،  دو پوشه تعریف میکنیم ، پوشه های Models و Services  و داخل اونها بر اساس انتیتتی هایی که داریم ( بر اساس مقاله قبلی در این مثال دو موجودیت User و UserExam وجود دارن  ) پوشه های جدا میسازیم  در نهایت باید پوشه بندی ما مثل شکل زیر باشه.در پوشه Model ، مدل های کاری ما قرار میگیره ، مثلا DTO ها برای انتقال دیتا و یا DtoValidation ها برای اعتبار سنجی مدل های انتقال دیتا مون ، فعلا با پوشه ی Model کاری نداریم. میرسیم به پوشه ی Services که پیاده سازی عملیات های مورد نیاز ماست ، در این قسمت برای هر موجودیت یک کلاس اینترفیس و یک کلاس برای خود سرویس تعریف میکنیم ، مانند شکل زیرخب ، میریم برای نوشتن سرویس هامون ، اما در حال حاظر نمیدونیم که قرار چه عملیات هایی رو تو ای سرویس ها میخوایم پیاده کنیم ، باید جلوتر بریم تا عملیات هامون رو بدونیم و تو سرویس خودش پیاده کنیم ، مثلا برای ثبت نام کاربر ما نیاز به متد Register داخل سرویس UserService داریم .قبل از پیاده سازی متد های سرویس هامون نیاز به پیاده سازی یک ریپازیتوری  برای ذخیره و بازیابی اطلاعاتمون داریم ، بنابرین میریم که ریپازیتوری هامون رو داخل لایه ی 02.Infrastructures داخل پروژه ی DataLayer.SqlServer پیاده سازی کنیم .از اینجا شروع به پیاده سازی لایه DataLayer میکنیم.در نظر داشته باشید که قالب یا همون اینترفیس ریپازیتوری هامون ( IRepository  ) رو قبلتر داخل Core و پروژه ی DomainClass مون نوشتیم و باید از اون فایل ارث بری کرده و پیاده سازی کنیم.از اونجا که قصد داریم برای دیتا بیسمون از SqlServer و ORM قدرتمند EF استفاده کنیم میریم برای اینکه پکیج های مورد نیازمون رو از نوگت نصب کنیم.دو پکیج زیر رو به پروژه ی DataLayer.SqlServer که داخل پوشه ی  02.Infrastructures هست رو اضافه میکنیم.Microsoft.EntityFrameworkCore.SqlServerMicrosoft.EntityFrameworkCore.Toolsپکیج Microsoft.EntityFrameworkCore.SqlServer برای کار با SqlServer و Ef هستپکیج Microsoft.EntityFrameworkCore.Tools یکسری ابزار برای انجام کارمون مثل دستورات add migration و update database در اختیارمون میزاره.بعد از نصب پکیج ها میریم تا پوشه بندی لایه ی DataLayer در داخل پروژه ی DataLayer.SqlServer انجام بدیم.یک پوشه Common تعریف میکنیم برای کلاس های عمومی تر مثل ApplicationContext که پل ارتباطی با دیتا بیس هستش و کلاس EFRepository که قراره از IRepository که داخل لایه ی DomainClass مون هستش ارث بری کنه و متد های مورد نیازمون رو پیاده سازی کنه.یک پوشه به نام EfConfiguration که برای کلاس های تنظیمات دیتا بیسی ماست ، به ازای هر انتیتی تنظیماتی که میخواهیم در دیتا بیس انجام بدیم رو توسط کلاس ها داخل این پوشه تعریف میکنیم پس باید برای هر موجودیت هامون که داخل دیتا بیس جدول دارن یک کلاس برای تنظیمات بسازیم.منظور از تنظیماتی مثل نوع داده ، طول فیلد و ... هستشدر آخر هم پوشه به نام Repositories میزاریم که قرار هستش برای هر انتیتی عملیات داخل اینترفیسی که داخل DomainClass برای هر موجودیت رو تعریف کردیم توسط EfRepository پیاده کنه.مثلا برای ازمون های کاربران اینترفیس IUserRepository رو داخل Domain تعریف کردیم که قراره توسط کلاس UserRepository ارث بری بشه و عملیات های داخل اون پیاده سازی بشه.در نهایت پوشه ها و کلاس های ما داخل پروژه DataLayer.SqlServer شکل زیر رو تشکیل میده.در ادامه کلاس ApplicationContext رو کامل میکنیم .ApplicationContext.cspublic class ApplicationContext : DbContext
    {
        public DbSet&lt;UserExam&gt; UserExams { get; set; }
        public DbSet&lt;User&gt; Users { get; set; }
        public ApplicationContext(DbContextOptions options) : base(options)
        {
        }
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {       
         modelBuilder.ApplyConfigurationsFromAssembly(typeof(ApplicationContext).Assembly);
            modelBuilder.Entity&lt;UserExam&gt;().HasQueryFilter(c =&gt; c.IsDeleted == false);
            base.OnModelCreating(modelBuilder);
        }
    }در این کلاس از DbContext ارث بری کردیم ، در EF برای اینکه پل ارتباطی یا همون کانتکست به دیتا بیس رو مشخص کنیم باید کلاسی داشته باشیم که از DbContext  ارث بری کرده باشه. برای مشخص کردن جداول مون داخل دیتا بیس هر موجودیت رو به این شکل تعریف میکنیمpublic DbSet&lt;UserExam&gt; UserExams { get; set; }
public DbSet&lt;User&gt; Users { get; set; }در نظر داشته باشید که کلاس های UserExam و User که موجودیت های ما هستند داخل لایه ی Domain تعریف شدند.در ادامه یک متد سازنده برای این کلاس داریم تا بتونیم تنظیماتی مثل کانکشن استرینگ به دیتا بیسمون رو تعریف کنیم public ApplicationContext(DbContextOptions options) : base(options)
        {
        }و در آخر هم متد OnModelCreating رو بازنویسی یا override میکنیم تا یکسری کارها رو انجام بدیم.protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
         modelBuilder.ApplyConfigurationsFromAssembly(typeof(ApplicationContext).Assembly);
            modelBuilder.Entity&lt;UserExam&gt;().HasQueryFilter(c =&gt; c.IsDeleted == false);
            base.OnModelCreating(modelBuilder);
        }در این متد گفتیم که تنظیماتی که میخواهیم برای موجودیت ها یا همون جدول های دیتا بیس لحاظ کردیم رو از فضای نام همین قسمت بخونmodelBuilder.ApplyConfigurationsFromAssembly(typeof(ApplicationContext).Assembly);در ادامه تعریف کردیم که موجودیت آزمون ما در صورتی که فیلد IsDeleted آن False بود درنظر بگیر ، این مورد برای همان SoftDelete هست که در قسمت اول گفتیمmodelBuilder.Entity&lt;UserExam&gt;().HasQueryFilter(c =&gt; c.IsDeleted == false);در آخر هم متد کلاس پایه رو فراخونی کردیم تا کار ادامه پیدا کنه base.OnModelCreating(modelBuilder);خب میریم سراغ پوشه EfConfiguration برای تنظیمات دیتا بیسقبلتر هم گفتیم که به ازای هر موجودیت که قراره جدولی داخل دیتا بیسمون بشه کلاس میسازیم و تنظیماتمون رو داخلش اعلام میکنیم.مثل کلاس زیر برای تنظیمات کاربر یا موجودیت UserUserConfiguration.cspublic class UserConfiguration : IEntityTypeConfiguration&lt;User&gt;
    {
        public void Configure(EntityTypeBuilder&lt;User&gt; builder)
        {
            builder.Property(cb =&gt; cb.Mobile)
              .IsRequired()
               .HasMaxLength(11);
            builder.Property(cb =&gt; cb.Name)
              .IsRequired()
               .HasMaxLength(100);

            builder.Property(cb =&gt; cb.Password)
            .IsRequired()
             .HasMaxLength(100);

        }
    }این حالت که برای تنظیمات دیتا بیس کلاس جدا تعریف میکنیم  در واقع Fluent Api میگن که خودش مبحث بزرگیه ، در کل روش های دیگه ای برای تعریف تنظیمات دیتا بیسی داخل EF وجود داره ولی تمیز ترین حالت همین کاره.در اینجا به عنوان مثال  توسط IsRequired و HasMaxLength ، اجباری بودن و حداکثر طول فیلد رو مشخص کردیم.برای موجودیت آزمون کاربران هم همین کار رو انجام میدیم.خب الان که کلاس ApplicationContext که همون پل ارتباطمون با دیتا بیس هستش و همچنین تنظیماتی که روی جداول دیتا بیسمون لازم داریم رو ساختیم میتونیم کلاس EFRepository که قراره اینترفیس IRepository که داخل DomainClass مون تعریف شده رو پیاده کنه بسازیم.کلاس EFRepository به این شکل میشهEFRepository.cspublic class EfRepository&lt;TEntity&gt; : IRepository&lt;TEntity&gt; where TEntity : BaseEntity, new()
    {
        protected readonly ApplicationContext dbContext;
        public EfRepository(ApplicationContext dbContext)
        {
            this.dbContext = dbContext;
        }
        public void Insert(TEntity entity)
        {
            dbContext.Add(entity);
            dbContext.SaveChanges();
        }
        public void Remove(TEntity entity)
        {
            dbContext.Remove(entity);
            dbContext.SaveChanges();
        }
        public TEntity Get(int id)
        {
            return dbContext.Set&lt;TEntity&gt;().FirstOrDefault(c =&gt; c.Id == id);
        }
        public IEnumerable&lt;TEntity&gt; GetAll()
        {
            return dbContext.Set&lt;TEntity&gt;().ToList();
        }
        public void Update(TEntity entity)
        {
            dbContext.Update(entity);
            dbContext.SaveChanges();
        }
        public void SaveChanges()
        {
            dbContext.SaveChanges();
        }
        public IQueryable&lt;TEntity&gt; GetQueryable()
        {
            return dbContext.Set&lt;TEntity&gt;().AsQueryable();
        }
    }کلاس EFRepository که از کلاس IRepository  ارث بری کرده و شرط ارث بری از BaseEntity که داخل لایه Domain مون هستش رو هم داره.پیاده سازی متد ها یی که داخل اینترفیس IRepository   هستش رو توسط EF با کلاس ApplicationContext  که پل ارتباطیمون با دیتا بیس هست و داخل سازنده ی کلاس تزریق شده رو  بصورت جنریک میبینیم.حالا میتونیم برای هر موجودیتی که داریم ریپازیتوری تعریف کنیم ، به این شکل که کلاس های UserExamRepository و UserRepository رو داخل پوشه ی Repositories در لایه ی DataLayer و پروژه ی DataLayer.SqlServer میسازیم.در حال حاظر متد هایی که در EfRepository تعریف و پیاده شدند برای این دو کلاس کافی هستش و از کلاس EfRepository  هم ارث بری میکنیم و به این شکل کلاس UserRepository و UserExamRepository رو میسازیمUserExamRepository.cs public class UserExamRepository : EfRepository&lt;UserExam&gt;, IUserExamRepository
    {
        public UserExamRepository(ApplicationContext DbContext) : base(DbContext)
        {
        }
    }UserRepository.cspublic class UserRepository : EfRepository&lt;User&gt;, IUserRepository
    {
        public UserRepository(ApplicationContext DbContext) : base(DbContext)
        {
        }
    } ممکن هستش در آینده متد های بیشتری بر اساس نیاز سرویس هامون بخوایم اضافه کنیم به ریپازیتوری ها که در آینده این کار رو انجام میدیم.تا اینجا ریپازیتوری هامون رو هم اضافه کردیم و لایه ی DataLayer کامل میشه.در ادامه نیاز داریم سرویس های مورد نیازمون که در لایه نهایی یا همون EndPoint لازم داریم رو بنویسیم ، به عنوان مثال میخوایم کاربر جدید ثبت کنیم بنابرین سرویس یوزر داخل کلاس UserService باید پیاده سازی ثبت نام رو داشته باشه ( یوزر جدید داخل دیتا بیس ثبت بشه ) تا داخل EndPoint  هامون ازش استفاده کنیم.ادامه کار رو در مقاله بعدی با پیاده سازی EndPoint که یک RestApi هستش  در ادامه خواهیم دید.البته هنوز کلی از کارمون مونده ولی سورس کامل این مقالات رو میتونید از اینجا ببینید.امیدوارم از این مطلب خوشتون اومده باشه :)پیاده سازی پروژه API در دات نت ، قسمت اول ، معماری پیاز (Onion Architecture)پیاده سازی پروژه API در دات نت ، قسمت دوم ، ApplicationServices و DataLayerپیاده سازی پروژه API در دات نت ، قسمت سوم ، EndPoint توسط RestAPIپیاده سازی پروژه API در دات نت ، قسمت چهارم ، RestAPI Authentication Jwtپیاده سازی پروژه API در دات نت ، قسمت پنجم ، عملیات CRUD در RestAPIپیاده سازی پروژه API در دات نت ، قسمت ششم ( قسمت آخر ) ، عملیات CRUD در RestAPI پارت دوم</description>
                <category>رضا منصوری</category>
                <author>رضا منصوری</author>
                <pubDate>Tue, 07 Jun 2022 22:27:54 +0430</pubDate>
            </item>
                    <item>
                <title>پیاده سازی پروژه API در دات نت ،  قسمت اول ، معماری پیاز (Onion Architecture)</title>
                <link>https://virgool.io/@reza.mansouri/%D9%BE%DB%8C%D8%A7%D8%AF%D9%87-%D8%B3%D8%A7%D8%B2%DB%8C-%D9%BE%D8%B1%D9%88%DA%98%D9%87-%D8%A8%D8%A7-%D9%85%D8%B9%D9%85%D8%A7%D8%B1%DB%8C-%D8%AA%D9%85%DB%8C%D8%B2-%D8%AF%D8%B1-%D8%AF%D8%A7%D8%AA-%D9%86%D8%AA-%D9%82%D8%B3%D9%85%D8%AA-%D8%A7%D9%88%D9%84-pvanh3o1xzqn</link>
                <description>سلام :) تصمیم دارم تو چند مقاله با هم به نوشتن یک پروژه کوچیک و البته با معماری پیاز (Onion Architecture)   کنیم تا مقداری با هم در این مورد تبادل اطلاعات کنیم ، پیاده سازی پروژه API در دات نت ، قسمت اول ، معماری پیاز (Onion Architecture)پیاده سازی پروژه API در دات نت ، قسمت دوم ، ApplicationServices و DataLayerپیاده سازی پروژه API در دات نت ، قسمت سوم ، EndPoint توسط RestAPIپیاده سازی پروژه API در دات نت ، قسمت چهارم ، RestAPI Authentication Jwtپیاده سازی پروژه API در دات نت ، قسمت پنجم ، عملیات CRUD در RestAPIپیاده سازی پروژه API در دات نت ، قسمت ششم ( قسمت آخر ) ، عملیات CRUD در RestAPI پارت دوممواردی که گفته میشه ، نظر من هستش و ممکنه شما نظر متفاوتی داشته باشید که برای من محترم هست و میتونید در قسمت نظرات بنویسید :) مثال ما از نوع REST API  هستش که امروزه خیلی استفاده میشه .تو این مثال ما کاربر هم تعریف میکنیم و برای عملیات ذکر شده نیاز به احراز هویت ( توسط JWT ) هستش که در مقالات بعدی میبینیم.خب ابتدا بریم تا یک سناریو ساده برای شروع کارمون داشته باشیم تا نقطه شروعمون همین باشه.سناریو ما اینه که قرار هست یکسری آزمون تعریف کنیم و عملیات CRUD ( ساختن ، خوندن ، ویرایش و حذف ) رو برای نمونه انجام بدیم ، قسمت کاربران هم داریم که به آزمون ها متصل هستند .ما از NET 6.  برای ساخت پروژه هامون استفاده میکنیم.سورس کامل این مقالات رو میتونید از اینجا ببینید.برای شروع یک سلوشن خالی ایجاد میکنیم تا ساختار و پوشه های پروژه رو تعریف کنیم.شروع به اضافه کردن پوشه ها برای ساختارمون میکنیمابتدا سه پوشه به نام های Documents SrcTestsایجاد می‌کنیم ، که همونطور که از اسم هاشون مشخص هست برای فایل های مستندات ، سورس کد هامون و تست هایی که مینوسیم هستش.در ادامه در پوشه ی Src که سورس کد هامون هستش پوشه های زیر رو اضافه میکنیم01.Core02.Infrastructures03.EndPointاعدادی که ابتدای نام ها گذاشتیم برای اینه که ترتیب نمایش پوشه ها رو بتونیم مشخص کنیم.در این نمونه سعی میکنیم که از ساختار Onion Architecture استفاده کنیم ، در نظر داشته باشید که پیاده سازی ها ممکنه متفاوت باشه و جای دیگه ای ساختار به این شکل نباشه.مهمترین دلیل برای ایجاد چنین معماری ای، نیازمندی به ساختاری است تا قابلیت نگهداری برنامه ها در دراز مدت را فراهم نماید. این مورد با رعایت اصل Separation Of Concerns در سرتاسر سیستم به دست می آید.نکته حائز اهمیت در رابطه با Onion Architecture این است که این معماری برای پروژه های ساده و سبک اصلا مناسب نیست بلکه برای برنامه های بزرگ با رفتارهای پیچیده مناسب می باشد.معماری پیاز یکی از بهترین معماری های موجود برای پیاده سازی Testability ( قابل تست بودن )، Maintainability ( قابلیت نگهداری ) و Dependability ( قابلیت اطمینان ) در ساختار نرم افزار می باشد.پوشه ی اول یا همون Core ، هسته ی اپلیکیشن ما هست ، در این پوشه دو پروژه از نوع  Class Librery به نام های ApplicationServices و DomainClass ایجاد میکنیم.برای پوشه ی 02.Infrastructures هم یک پروژه از نوع Class Librery  ایجاد کرده و از اونجا که در این مثال از SqlServer میخوایم استفاده کنیم نام اون رو DataLayer.SqlServer میزاریم که فعلا با این قسمت کاری نداریم . در نظر داشته باشید که در این ساختار ما محدود به استفاده از ابزار دیتابیسی خاصی نیستیم ، مثلا میتونیم از Mongo و یا هر دیتابیس دیگه هم استفاده کنیم و اگر چنین قصدی داشته باشیم یک پروژه  دیگه به نام DataLayer.Mongo تعریف میکنیم .در آخر برای در پوشه ی 03.EndPoint که لایه ی آخر ما هست و به اون لایه نمایش یا Persentation هم میگن یک پروژه از نوع RestApi ایجاد میکنیم و فعلا با این قسمت هم کاری نداریم ، در نظر داشته باشید که پروژه ی ما ممکنه فقط یک EndPoint  نداشته باشه و مثلا بخوایم یک خروجی وب سایت از پروژمون هم داشته باشیم  در این صورت می‌تونیم اینجا پروژه های بیشتری بر اساس خروجی ها مون تعریف کنیم .خب ساختار پوشه بندی و پروژه های ما تا اینجا باید مثل شکل زیر باشهدر معماری  Onion Architecture ، لایه ی Domain، داخلی ترین لایه بوده و به هیچ لایه ی بیرونی وابستگی ندارد. این معماری با تکیه بر اصل Dependency Inversion تمام وابستگی های لایه ی Domain را در قالب Interface ها در اختیار آن قرار می دهد تا این لایه از جزئیات پیاده سازی و وابستگی به ابزارهای زیر ساختی در امان باشد.سراغ پروژه ی ( Class Librery ) DomainClass که داخل پوشه ی Src 01.Core هستش میریم و سه پوشه ی زیر رو بهش اضافه میکنیم.CommonUserUserExamپوشه ی Common برای تعریف کلاس های عمومی پایه ی ما هستش و پوشه های دیگه بر اساس موجودیت هایی که داریم کلاس های آنها را تعریف میکنیم در اینجا دو موجودیت کاربر و آزمون کاربر رو داریم .سراغ پوشه ی Common میریم و دو کلاس زیر رو بهش اضافه میکنیمBaseEntity.csIRepository.csکلاس BaseEntity برای تعریف کلاس پایه برای موجودیت هامون هستش ، یا این کار کد های تکراریمون حذف میشه و در آینده تغییراتمون راحتتر انجام میشه BaseEntity.cspublic class BaseEntity
 {
        [Key]
        public long Id { get; set; }
        public DateTime CreatedDate { get; set; }
        public DateTime ModifiedDate { get; private set; }         
        public BaseEntity()
        {
            ModifiedDate = DateTime.Now;
        }
    }خب از اسم ها مشخص هستش که هرکدوم برای چه کاری هستند. Id از نوع Long به عنوان کلید اصلی CreatedDate تاریخ ایجاد موجودیت و ModifiedDate تاریخ آخرین ویرایش هستش که البته ویرایش اون private شده و توسط خود کلاس میتونه تغییر کنه ، همچنین در سازنده ی کلاس هم مقدار ModifiedDate  رو تاریخ و زمان جاری میزاریم تا خودکار مقدار تاریخ جاری رو بگیره.این کلاس پایه برای موجودیت هامون هستش و توسط اونها ارث بری میشه.کلاس دوم به نام IRepository.cs که یک اینترفیس یا همون قرارداد هستش که مشخص میکنه مخزن داده ها یا همون ریپازیتوری هامون که قراره ازشون دیتا بخونیم قراره چه متد هایی رو پیاده سازی کنند .این کلاس هم توسط کلاس های ریپازیتوری هایی که در آینده می‌خوایم ایجاد کنیم ارث بری میشن .IRepository.cspublic interface IRepository&lt;TEntity&gt; where TEntity : BaseEntity, new()
    {
        void Insert(TEntity entity);
        void Remove(TEntity entity);
        void Update(TEntity entity);
        void SaveChanges();
        TEntity Get(int id);
        IEnumerable&lt;TEntity&gt; GetAll();
        IQueryable&lt;TEntity&gt; GetQueryable();
    }در این کلاس از جنریک ها استفاده کردیم تا برای ارث بری و پیاده سازی برای کلاس های مختلف مشکلی نداشته باشیم همچنین در حالت ارث بری از این کلاس شرط گذاشتیم که حتما کلاس باید از BaseEntity که قبلتر ساختیم ارث بری کرده باشه.متد هایی که وجود دارند هم از اسمشون کاربرد اونها مشخص هستش ، مثل اضافه کردن ، ویرایش ،حذف ، ذخیره ی تغییرات و خوندن اونهاست.خب از اینجا به بعد شروع به پیاده سازی Domain های موجودیت های خودمون میکنیم ، یعنی User و UserExamUser.cspublic class User : BaseEntity
    {
        public string Mobile { get; set; }
        public string Name { get; set; }
        public string Password { get; set; }
    }خب کلاس User که برای موجودیت کاربران هستش و داخل پوشه ی User میسازیم ، همونطور که قبلتر گفتم از کلاس پایه مون یا همون BaseEntity ارث بری میکنیم تا پراپرتی های عمومی مثل ID و CreateDate  و .. خودکار به کلاس اضافه بشن.علاوه بر اونها پراپرتی هایی مثل شماره موبایل و نام و پسورد هم برای این موجودیت تعریف میکنیم.میریم سراغ موجودیت بعدی که ازمون های کاربران هستش.UserExam public class UserExam : BaseEntity
    {
        public string Name { get; set; }
        public User User { get; set; }
        public DateTime? StartDate { get; set; }
        public DateTime? EndDate { get; set; }
        public bool IsDeleted { get; set; }
    }خب این کلاس رو در پوشه ی UserExam اضافه میکنیم و از  کلاس پایمون ارث بری میکنیم و پراپرتی هایی برای نام ، کاربری که به این آزمون مرتبط است تاریخ شروع و تاریخ پایان و همچنین یک پراپرتی به نام IsDeleted برای حالت حذف آن ( Soft Delete )  اضافه میکنیم منظور از حذف منطقی اینه هستش که ما بشکل فیزیکی از روی حافظه ( دیتا بیس یا هر منبع ذخیره سازی ) موجودیت رو پاک نمیکنیم بلکه توسط یک نشونه یا flag اعلام میکنیم که این مورد از نظر ما حذف شده و استفاده نمیشه ولی بنا به دلایلی با یک نشونه این حالت رو در نظر میگیریم .خب میرسیم به تعریف قرارداد های ریپازیتوریمون یا منبع داده هامون ، اینکه از هر نوع دیتا بیسی استفاده میکنیم از این قرارداد ها ارث بری بشه تا پیاده سازی عملیات هایی که نیاز داریم مشخص باشه.برای این کار از اینترفیس پایه که برای ریپازیتوری هامون نوشتیم استفاده میکنیم تا از تکرار کد جلوگیری کنیم و تغییرات آیندمون راحتتر باشه ، به این شکل .برای اینترفیس ریپازیتوری کاربرانIUserRepository.cs public interface IUserRepository : IRepository&lt;User&gt;
    {
    }میبینید که هیچ پیاده سازی بیشتری در حال حاظر برای اینترفیسمون نداریم و همون متد های کلاس پایمون کارمون رو راه میندازه ، البته فعلا اینطوره.برای موجودیت آزمون کاربران هم همین کار رو انجام میدیم ، تعریف یک اینترفیس و ارث بری از کلاس پایه اینترفیس ریپازیتوریمونIUserExamRepository.cs public interface IUserExamRepository : IRepository&lt;UserExam&gt;
    {
    }در نهایت باید شکل سلوشنمون تا اینجا به این شکل باشهخب تا اینجا قسمت DomainClass هامون تکمیل شده .شاید اینجا سوال پیش بیاد که آیا واقعا نیاز به این همه تعریف کلاس های پایه ، ارث بری از اینترفیس های پایه و ... هستش!!در جواب بله ، شاید در نگاه اول نیاز به خیلی از این موارد نباشه ولی رعایت این موارد در توسعه ی پروژه بسیار کمک میکنه و مهم تر از اون کد تمیز و توسعه پذیری داریم که تمام قواعد اون مشخص هستش ، در مقاله بعدی میریم سراغ قسمت ApplicationServices و کارمون رو ادامه میدیم. البته هنوز کلی از کارمون مونده ولی سورس کامل این مقالات رو میتونید از اینجا ببینید.امیدوارم از این مطلب خوشتون اومده باشه :)</description>
                <category>رضا منصوری</category>
                <author>رضا منصوری</author>
                <pubDate>Mon, 06 Jun 2022 22:23:35 +0430</pubDate>
            </item>
                    <item>
                <title>CI / CD ساده با GitHub در دات نت</title>
                <link>https://virgool.io/@reza.mansouri/ci-cd-%D8%B3%D8%A7%D8%AF%D9%87-%D8%A8%D8%A7-github-%D8%AF%D8%B1-%D8%AF%D8%A7%D8%AA-%D9%86%D8%AA-apchjmadewfh</link>
                <description>گیت‌هاب  برای راه اندازی CI/CD از GitHub Actions استفاده می کند که می توانید مستندات آن را در اینجا مشاهده کنید و به صورت کامل با آن آشنا شوید. در اینجا با یک مثال با مراحل راه‌اندازی CI/CD به وسیله GitHub آشنا می شویم.خب ابتدا باید یک پروژه داشته باشیم تا بتونیم کارمون رو شروع کنیم.یه پروژه نمونه میسازیم ، از اونجا که خودم دات نت کار هستم پروژه ی نمونه رو با دات نت کور میسازم و و صد البته یک تست هم می‌نوسیم که بتونیم تو روند CI / CD تست رو هم اجرا کنیم . در واقع بنظرم بدون تست ، روند CI CD ارزش خودش رو نشون نمیده ، باید تست های ما با موفقیت پاس بشن تا مطمئن باشیم که ورژنی که میخوایم پابلیش کنیم درست کار میکنه و مشکلی نداره.پروژه ی نمونه رو ساختیم و یک تست نمونه هم گذاشتیم تا روند کار را ببینیم.یک پروژه ساده و یک تست سادهچون قصدمون اینه که روند CI CD رو ببینیم تستمون هم ساده مینیویسیم .  public class SampleTest
    {
  [Fact]
        public void Just_For_Run_Sample_Test()
        {
            // Arrange
            var myvar = &amp;quottest&amp;quot
            //Act
            bool result= (myvar == &amp;quottest&amp;quot);
            //Assert 
            Assert.True(result); 
        }
    }میبینیم که تستمون که هیچ بیسینسی نداره و پاس میشه .خب پروژمون رو داخل گیت‌هاب اضافه میکنیم تا کارمون رو ادامه بدیم.پروژه ی نمونه رو از این لینک می‌تونید ببینید.داخل ریپازیتوری که ساختیم در سایت گیت هاب به تب Action میریم و دات نت رو انتخاب می‌کنیم.دقت کنید برای استفاده از این سرویس می‌تونیم از زبان ها و پلتفرم های مختلف استفاده کنیم ، چون نمونه پروژه ی ما دات نت هست این گزینه رو انتخاب می‌کنیم.روی دکمه ی Configure کلیک میکنیم تا گیتهاب نمونه کدی که برای تنظیمات داره رو برای ما بسازه. همونطور که مشاهده میکنید داخل پوشه ی .github workflows یک فایل با نام dotnet.yml می‌سازه و کافیه که ما تنظیمات مد نظرمون رو داخل این فایل بنویسیم .فایل با پسوند yml و برای تنظیماتمون هستش.برای پروژه ی نمونه از کد زیر استفاده می‌کنیم..github/workflows/dotnet.ymlname: .NET
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build-test-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: ? Setup .NET
uses: actions/setup-dotnet@v2
with:
dotnet-version: 6.0.x
- name: ? Restore dependencies
run: dotnet restore
- name: ? Build
run: dotnet build --no-restore
- name: ? Test
run: dotnet test --no-build --verbosity normal
- name: ? Sync files
uses: SamKirkland/FTP-Deploy-Action@4.3.0
with:
server: ${{ secrets.ftp_server }}
username: ${{ secrets.ftp_user }}
password: ${{ secrets.ftp_password }}ابتدای فایل همونطور که مشخصه ‌، گفتیم که عملیات در چه اکشن هایی انجام بشهon:
push:
branches: [ master ]وقتی پوش کردیم روی برنچ مستر عملیات شروع میشهدر ادامه یک جاب ساختیم و بهش نام build-test-deploy رو دادیم که منظورمون اینه که در این جاب چه عملیات هایی انجام میشهjobs:
build-test-deploy: و مهم ترین قسمت مراحل کارمون یا همون  step هامون هستش که به نوبت اجرا میشه و مراحل زیر رو داریم:? Setup .NETبه گیت‌هاب میگیم که پروژمون رو چه کدی هست و ورژن اون چیهuses: actions/setup-dotnet@v2
with:
dotnet-version: 6.0.x? Restore dependenciesکه پکیج هایی که داخل پروژه هستند رو میگیره run: dotnet restore? Buildپروژه رو بیلد میکنه ( اگر در همین مرحله خطایی باشه کار متوقف میشه و ادامه پیدا نمیکنه )run: dotnet build --no-restore? Testتست هامون رو اجرا میکنه ( قسمت مهم کار که تست های خودکاری که نوشتیم باید پاس بشه تا روند ادامه پیدا کنه )run: dotnet test --no-build --verbosity normalو در آخر ? Sync filesuses: SamKirkland/FTP-Deploy-Action@4.3.0
with:
server: ${{ secrets.ftp_server }}
username: ${{ secrets.ftp_user }}
password: ${{ secrets.ftp_password }}که در صورت پاس شدن تمام مراحل فایل هامون رو به آدرس FTP که بهش دادیم منتقل میشه.تظیمات FTP رو توسط secret تعریف کردیم تا بتونیم استفاده کنیم ولی دیده نشن.منظورمون از ${{ secrets.ftp_password }} اینه که گیت هاب این مقدار رو از داخل secret هایی که براش تعریف کردیم بخونه.برای تعریف secret به قسمت Settings   &gt; Secrets &gt; Actions رفته  و روی New repository secret میزنیم سپس نام متغیر خود به عنوان مثال ftp_password وارد کرده و مقدار آن را نیز وارد میکنیم و در کد تنظیماتمون به این شکل میتوان مقدار آن را برگردونیم${{ secrets.ftp_password }}با این کار مقادیر ما خوانده میشود ولی به هیچ عنوان قابل دسترس و دیده شدن نیستند.اگر گیت‌هاب تو هریک از مراحل به مشکلی بر بخوره روند متوقف میشه و ادامه پیدا نمیکنه.در آخر پروژه ما پابلیش میشه.خب فایل رو کامیت میکنیم و خودکار پوش میشه .فایل در آدرس .github/workflows/dotnet.yml اضافه میشه و اینکه حتما یادمون باشه مقادیر ftp_server ftp_user ftp_password  رو حتما تو قسمت Settings   &gt; Secrets &gt; Actions  بسازیم و تعریف کنیم. میریم که پوش کنیم و ببینیم چه اتفاقی میافته . :)خب اولین پوش رو انجام دادیم و  GitHub Actions کارش رو شروع میکنهبه تب Action میریم و میبینیم که به خطا خوردیمبا کلیک و مشاهده مشاهده جزییات خطا رو می‌تونیم ببینیمتو قسمت ? Sync files که برای انتقال فایل ها به FTP هستش به خطایError: Error: Input required and not supplied: serverخوردیم و بخاطر اینه که secret رو برای server  تعریف نکردیم و مقدار ندادیم.همونطور که میبینید گیت‌هاب تمام مراحل رو نشون میده با جزییات و لاگ که می‌تونید لاگ اونها با کلیک روی هر مرحله ببینید.در ضمن ایمیلی هم براتون ارسال میشه و اطلاع میده که به خطا خوردیم.برای رفع مشکل secret ها رو تعریف میکنیم و دوباره اجرا می‌کنیم.دقت کنید برای اجرای مجدد هم می‌تونید یک پوش انجام بدین و هم اینکه داخل اکشن ها دوباره دکمه ی اجرای مجدد رو بزنید.ما یه پوش ساده میزنیم داخل خود پروژه تا نتیجه رو ببینیمبعله  :) می‌بینیم که اکشن با موفقیت اجرا شد و ورژن ما پابلیش شد ، اونم فقط با یه پوش ساده.از این به بعد کافیه که فقط تغییراتمون رو پوش کنیم تا ورژن ما روی سرور بره ، به همین راحتی.اما در نظر داشته باشید که هرکدوم از مراحل انجام نشه ، ورژن ما هم نمیره ، مثلا تست های ما پاس نشن ، برای مثال نتیجه ی تست رو من false پاس میدم ( یه  ! کنارش میزارم ) و پوش میکنم  تا نتیجه رو ببینیم.Assert.True( ! result); نتیجه میشهتست من پاس نشد و درنتیجه ورژن من هم پابلیش نشد.البته دوباره تست رو درست میکنم تا پاس بشه.امیدوارم این مطلب رو دوست داشته باشید و بدردتون بخوره :) .نمونه کد این مطلب با روند انجامش رو می‌تونید از اینجا بگیرید . </description>
                <category>رضا منصوری</category>
                <author>رضا منصوری</author>
                <pubDate>Mon, 30 May 2022 21:00:58 +0430</pubDate>
            </item>
                    <item>
                <title>مشکل منابع در استفاده از داکر ویندوز</title>
                <link>https://virgool.io/@reza.mansouri/%D9%85%D8%B4%DA%A9%D9%84-%D9%85%D9%86%D8%A7%D8%A8%D8%B9-%D8%AF%D8%B1-%D8%A7%D8%B3%D8%AA%D9%81%D8%A7%D8%AF%D9%87-%D8%A7%D8%B2-%D8%AF%D8%A7%DA%A9%D8%B1-%D9%88%DB%8C%D9%86%D8%AF%D9%88%D8%B2-sfv1mah9vlav</link>
                <description>سلام :)اگر مثل من عاشق ویژوال استودیو و داکر با هم باشید باید از داکر در ویندوز استفاده کنید ، این کار با استفاده از سرویس WSL در ویندوز امکان پذیر هست.سرویس WSL 2 ( که نسخه جدید تر WSL هستش ) در ویندوز این امکان رو به ما میده تا هسته ی لینوکس رو روی ویندوز داشته باشیم و ازش استفاده کنیم که برای استفاده از داکر یا همون Docker Desktop در ویندوز باید این سرویس نصب باشه.در این تصویر نوتیفیکیشن ویندوز رو میبینیم که نشون میده داکر در حال ران شدن به همراه WSL 2 هستشاما مسئله ای که وجود داره اینه که سرویس WSL 2  میتونه  بصورت داینامیک منابع رو در اختیار بگیره و این در بیشتر موارد باعث میشه وقتی ما با Docker Desktop کار میکنیم و کانتینر‌های خودمون رو بالا میاریم از منابع سیستممون استفاده زیادی بشه، تا جایی که ما قادر انجام کار دیگه ای نیستیم.مثل این تصویر که پروسس Vmmem که مربوط به اجرای کانتینر های داکر هستش بیشترین منابع ( رم و سی پی یو ) رو اشغال کرده و ول کن هم نیست :)خب راه حل چیه؟راه حل محدود کردن سرویس WSL 2  برای استفاده از منابع هستش ، به این شکل که تعریف میکنیم که به چه میزان حداکثری از منابع استفاده کنه.برای این کار کافیه به یوزر خودمون در فولدر User های محل نصب ویندوزمون بریم و فایلی به نام .wslconfig بسازیم و محدویت های استفاده از منابع خودمون رو تعریف کنیم.بطور مثال به این شکلدر این مثال من در پوشه ی C:\Users\Reza فایل .wslconfig رو ساختم ( دقت کنید که دات اول کلمه ی .wslconfig هستش ) .wslconfig و برای استفاده از رم و پردازنده محدودیت تعریف کردم ، استفاده حداکثری از 2 گیگ از رم و 2 پردازنده مجازی از سی پی یو[wsl2]
memory=2GB   # Limits VM memory in WSL 2 up to 2GB
processors=2 # Makes the WSL 2 VM use two virtual processorsبعد از ایجاد فایل و تنظیمات داخل آن نیاز به دوباره اجرا کردن سرویس WSL 2 هستش تا تنظیمات اعمال بشه ( راحت ترین کار ریست کردن سیستم هستش )خب ریست میکنیم و میریم که ببینیم تنظیمات ما اعمال میشه یا نهمن همون کانتینر های قبلی ( که تست کردم و نزدیک 5 گیگ رم رو گرفته بود ) رو ران کردم و می‌بینیم که میزان استفاده از رم سیستم طبق محدودیتی که گذاشته بودم هست و بیشتر از 2 گیگ نمیشهبه این شکل می‌تونم در داکر ، کانتینر های مورد نیازم رو بالا بیارم و در کنارش از نرم‌افزار های دیگه هم استفاده کنم.امیدوارم که این مطلب به کارتون بیاد.نظراتتون باعث خوشحالیه من میشه :)</description>
                <category>رضا منصوری</category>
                <author>رضا منصوری</author>
                <pubDate>Fri, 04 Feb 2022 02:57:06 +0330</pubDate>
            </item>
            </channel>
</rss>