Ticketier project | ASP.NET Core Web API CRUD and Search | .NET 7 API | Full Course
Full Course Ticketier project with ASP.NET Core Web API (.NET 7 API ) and Entity Framework Core covering CRUD and Search step by step
In this video, we will create an ASP.NET Core Web API (.NET 7) project called Ticketier and implement full CRUD and Search functionality into it.
The focus of this project is to show you how you can build new ASP.NET Core Web API (.NET 7) project from 0 to 100 and implement CRUD and Search in it.
we will learn these topics together:
Entities
Dtos
Context
ORM
Http Methods
Swagger
AutoMapper
IQueryable
Where clause
حاصل قطع برق و یا یک ری استارت دستی ناصحیح را در نظر بگیرید:
Msdb از نوع دیتابیسهای سیستمی است و نمیشود مطابق روال متداول دیتابیسهای SUSPECT شده آنرا بازیابی کرد. این روش متداول به صورت زیر است:
DBCC checkdb('DBname')
ALTER DATABASE DBName SET SINGLE_USER WITH ROLLBACK IMMEDIATE
DBCC CheckDB ('DBName', REPAIR_ALLOW_DATA_LOSS)
ALTER DATABASE DBName SET MULTI_USER
در ابتدای کار دیتابیس در حالت اورژانسی قرار میگیرد. بعد وضعیت و میزان تخریب نمایش داده شده، سپس تک کاربره میشود. در ادامه به اس کیوال سرور اجازه داده میشود که دیتابیس را با هر وضعی (حتی به قیمت از دست رفتن تعدادی رکورد) ترمیم کند و در آخر دیتابیس مجددا به حالت چند کاربره بازگشت داده میشود.
این روشی است که سال قبل با قطعیهای مکرر برق زیاد کاربرد داشت.
اما دیتابیس سیستمی msdb را نمیشود در حالت اورژانسی قرار داد؛ بنابراین باید به دنبال راه چارهی دیگری بود. پس از مدتی جستجو در وبلاگهای msdn ، راه حل زیر یافت شد و کاملا عملی است (تست شده!) :
روش زیر در مورد اس کیوال سرور 2008 ، 2005 و حتی 2000 نیز قابل استفاده است.
ابتدا خونسردی خودتان را حفظ کنید! الان فقط دیگر با management studio نمیتوانید دیتابیسها را مرور کنید و همچنین تمام job های تعریف شده شما نابود شدهاند! اما سرور به کار عادی خودش میتواند ادامه دهد. سپس :
الف) تمام سرویسهای مربوط به اس کیوال سرور را stop کنید. به کنسول سرویسها مراجعه کرده و هر آنچه که در نام آن sql را مشاهده میکنید، stop کنید.
ب) با استفاده از خط فرمان، ابتدا به مسیر زیر وارد شوید:
و سپس دستور زیر را اجرا نمائید:
به این ترتیب اس کیوال سرور در یک حالت حداقل که بتوان دیتابیس msdb تخریب شده را detach کرد راه اندازی میشود. (پرچم 3608 مجوز detach کردن این دیتابیس را میدهد)
ج) management studio را اجرا کنید. زمانیکه پنجره کانکت ظاهر میشود آنرا کنسل کرده و در نوار ابزار بالای صفحه روی دکمه new query کیک کنید (چون حالت راه اندازی سرور در حالت تک کاربره است نمیخواهیم اتصال دیگری برقرار شود و در کار اخلال کند). با کلیک بر روی new query پنجره connect to server ظاهر میشود. در همین پنجره بر روی دکمه options کلیک کرده در برگه connection properties در قسمت connect to database نام master را وارد نمود و اکنون بر روی دکمه connect کلیک نمائید.
ج) سپس دستور زیر را وارد کنید تا دیتابیس msdb را بتوان detach کرد.
sp_detach_db 'msdb'
مراحلی که عنوان شد مهم است. اگر به این صورت عمل نکنید با پیغام خطای زیر مواجه خواهید شد:
اگر به این خطا برخوردید، یکبار دیگر از صفر شروع کنید. تمام سرویسهای مرتبط با sql را استاپ کنید (حتی در صورت نیاز کارت شبکه سرور را نیز غیرفعال کنید). و از مرحله الف مجددا شروع نمائید تا حتما حالت تک کاربرهی اتصال برقرار شود. (همچنین پنجرهی کوئری جدیدی را نیز باز نکنید چون در این حالت فقط و فقط یک اتصال مجاز است)
تا اینجا موفق شدیدم که دیتابیس msdb را detach کنیم. اکنون به پوشه دیتابیسها مراجعه کرده و mdf و ldf این دیتابیس تخریب شده را rename کنید (به هر اسمی که مایل بودید).
د) اکنون نوبت بازسازی مجدد این دیتابیس است.
محتویات فایل instmsdb.sql را که در مسیر C:\Program Files\Microsoft SQL Server\MSSQL10.MSSQLSERVER\MSSQL\install قرار دارد، در پنجرهی کوئری تک کاربرهای که در مرحله قبل بازکردهایم، copy/paste کرده و دکمه F5 را فشار دهید. پس از مدتی دیتابیس msdb باز سازی شده و مشکل برطرف میشود.
ه) اکنون سرور را stop و start کنید یا کلا کامپیوتر سرور را restart کنید تا تمامی سرویسهای stop شده راه اندازی مجدد شوند.
When cloud-based applications use other cloud-based services, errors can occur because of temporary conditions such as intermittent service, infrastructure-level faults, or network issues. Very often, if you retry the operation a short time later (maybe only a few milliseconds later) the operation may succeed
تعریف مدل سمت کاربر برنامه
فایل جدید Scripts\App\store.js را اضافه کرده و محتوای آنرا به نحو ذیل تغییر دهید:
var posts = [ { id: '1', title: "Getting Started with Ember.js", body: "Bla bla bla 1." }, { id: '2', title: "Routes and Templates", body: "Bla bla bla 2." }, { id: '3', title: "Controllers", body: "Bla bla bla 3." } ]; var comments = [ { id: '1', postId: '3', text: 'Thanks!' }, { id: '2', postId: '3', text: 'Good to know that!' }, { id: '3', postId: '1', text: 'Great!' } ];
سپس جهت استفاده از آن، تعریف مدخل آنرا به فایل index.html، پیش از تعاریف کنترلرها اضافه خواهیم کرد:
<script src="Scripts/App/store.js" type="text/javascript"></script>
ویرایش قالب مطالب برای نمایش لیستی از عناوین ارسالی
قالب فعلی Scripts\Templates\posts.hbs صرفا دارای یک سری عنوان درج شده به صورت مستقیم در صفحه است. اکنون قصد داریم آنرا جهت نمایش لیستی از آرایه مطالب تغییر دهیم.
همانطور که در تصویر ملاحظه میکنید، با درخواست آدرس صفحهی مطالب، router آن مسیریابی متناظری را یافته و سپس بر این اساس، template، کنترلر و مدلی را انتخاب میکند. به صورت پیش فرض، قالب و کنترلر انتخاب شده، مواردی هستند همنام با مسیریابی جاری. اما مقدار پیش فرضی برای model وجود ندارد و باید آنرا به صورت دستی مشخص کرد.
برای این منظور فایل Scripts\Routes\posts.js را به پوشهی routes با محتوای ذیل اضافه کنید:
Blogger.PostsRoute = Ember.Route.extend({ controllerName: 'posts', renderTemplare: function () { this.render('posts'); }, model: function () { return posts; } });
همچنین اگر به خاطر داشته باشید، در پوشهی کنترلرها فایل posts.js تعریف نشدهاست. اگر اینکار صورت نگیرد، ember.js به صورت خودکار کنترلر پیش فرضی را ایجاد خواهد کرد. در کل، یک قالب هیچگاه به صورت مستقیم با مدل کار نمیکند. این کنترلر است که مدل را در اختیار یک قالب قرار میدهد.
سپس مدخل تعریف این فایل را به فایل index.html، پس از تعاریف کنترلرها اضافه نمائید:
<script src="Scripts/Routes/posts.js" type="text/javascript"></script>
اکنون فایل Scripts\Templates\posts.hbs را گشوده و به نحو ذیل، جهت نمایش عناوین مطالب، ویرایش کنید:
<h2>Emeber.js blog</h2> <ul> {{#each post in model}} <li>{{post.title}}</li> {{/each}} </ul>
نمایش لیست آخرین نظرات ارسالی
در ادامه قصد داریم تا آرایه comments ابتدای بحث را در صفحهای جدید نمایش دهیم. بنابراین نیاز است تا ابتدا مسیریابی آن تعریف شود. بنابراین فایل Scripts\App\router.js را گشوده و مسیریابی جدید recent-comments را به آن اضافه کنید:
Blogger.Router.map(function () { this.resource('posts', { path: '/' }); this.resource('about'); this.resource('contact', function () { this.resource('email'); this.resource('phone'); }); this.resource('recent-comments'); });
Blogger.RecentCommentsRoute = Ember.Route.extend({ model: function () { return comments; } });
همچنین نیاز است تا تعریف مدخل این فایل جدید را نیز به انتهای تعاریف مداخل فایل index.html اضافه کنیم:
<script src="Scripts/Routes/recent-comments.js" type="text/javascript"></script>
اکنون قالب application واقع در فایل Scripts\Templates\application.hbs را جهت افزودن منوی مرتبط با این مسیریابی جدید، به نحو ذیل ویرایش خواهیم کرد:
<div class='container'> <nav class='navbar navbar-default' role='navigation'> <ul class='nav navbar-nav'> <li>{{#link-to 'posts'}}Posts{{/link-to}}</li> <li>{{#link-to 'recent-comments'}}Recent comments{{/link-to}}</li> <li>{{#link-to 'about'}}About{{/link-to}}</li> <li>{{#link-to 'contact'}}Contact{{/link-to}}</li> </ul> </nav> {{outlet}} </div>
<h1>Recent comments</h1> <ul> {{#each comment in model}} <li>{{comment.text}}</li> {{/each}} </ul>
<script type="text/javascript"> EmberHandlebarsLoader.loadTemplates([ 'posts', 'about', 'application', 'contact', 'email', 'phone', 'recent-comments' ]); </script>
نمایش مجزای هر مطلب در یک صفحهی جدید
تا اینجا در صفحهی اول سایت، لیست عناوین مطالب را نمایش دادیم. در ادامه نیاز است تا بتوان هر عنوان را به صفحهی متناظر و اختصاصی آن لینک کرد؛ برای مثال لینکی مانند http://localhost:25918/#/posts/3 به سومین مطلب ارسالی اشاره میکند. Ember.js به عدد 3 در اینجا، یک dynamic segment میگوید. از این جهت که مقدار آن بر اساس شماره مطلب درخواستی، متفاوت خواهد بود. برای پردازش این نوع آدرسها نیاز است مسیریابی ویژهای را تعریف کرد. فایل Scripts\App\router.js را گشوده و سپس مسیریابی post را به نحو ذیل به آن اضافه نمائید:
Blogger.Router.map(function () { this.resource('posts', { path: '/' }); this.resource('about'); this.resource('contact', function () { this.resource('email'); this.resource('phone'); }); this.resource('recent-comments'); this.resource('post', { path: 'posts/:post_id' }); });
با توجه به اینکه این مسیریابی جدید post نام گرفت (جهت نمایش یک مطلب)، به صورت خودکار، کنترلر و قالبی به همین نام را بارگذاری میکند. همچنین مدل خود را نیز باید از مسیریابی خاص خود دریافت کند. بنابراین فایل جدید Scripts\Routes\post.js را در پوشهی routes با محتوای ذیل اضافه کنید:
Blogger.PostRoute = Ember.Route.extend({ model: function (params) { return posts.findBy('id', params.post_id); } });
برای مثال، جهت آدرس http://localhost:25918/#/posts/3، مقدار post_id به صورت خودکار به عدد 3 تنظیم میشود.
پس از آن نیاز است مدخل این فایل جدید را در صفحهی index.html نیز اضافه کنیم:
<script src="Scripts/Routes/post.js" type="text/javascript"></script>
در ادامه برای نمایش اطلاعات مدل نیاز است قالب جدید Scripts\Templates\post.hbs را با محتوای زیر اضافه کنیم:
<h1>{{title}}</h1> <p>{{body}}</p>
<script type="text/javascript"> EmberHandlebarsLoader.loadTemplates([ 'posts', 'about', 'application', 'contact', 'email', 'phone', 'recent-comments', 'post' ]); </script>
اکنون به قالب Scripts\Templates\posts.hbs مراجعه کرده و هر عنوان را به مطلب متناظر با آن لینک میکنیم:
<h2>Emeber.js blog</h2> <ul> {{#each post in model}} <li>{{#link-to 'post' post.id}}{{post.title}}{{/link-to}}</li> {{/each}} </ul>
همچنین با کلیک بر روی هر عنوان نیز مطلب مرتبط نمایش داده خواهد شد:
افزودن امکان ویرایش مطالب
میخواهیم در صفحهی نمایش جزئیات یک مطلب، امکان ویرایش آنرا نیز فراهم کنیم. بنابراین فایل Scripts\Templates\post.hbs را گشوده و محتوای آنرا به نحو ذیل ویرایش کنید:
<h2>{{title}}</h2> {{#if isEditing}} <form> <div class="form-group"> <label for="title">Title</label> {{input value=title id="title" class="form-control"}} </div> <div class="form-group"> <label for="body">Body</label> {{textarea value=body id="body" class="form-control" rows="5"}} </div> <button class="btn btn-primary" {{action 'save' }}>Save</button> </form> {{else}} <p>{{body}}</p> <button class="btn btn-primary" {{action 'edit' }}>Edit</button> {{/if}}
در فرم تعریف شده، المانهای ورودی اطلاعات از handlebar helperهای ویژهی input و textarea استفاده میکنند؛ بجای المانهای متداول HTML. همچنین value یکی به title و دیگری به body تنظیم شدهاست (خواص مدل ارائه شده توسط کنترلر متصل به قالب). این مقادیر نیز داخل '' قرار ندارند؛ به عبارتی در یک handlebar helper به عنوان متغیر در نظر گرفته میشوند. به این ترتیب اطلاعات کنترلر جاری، به این المانهای ورودی اطلاعات به صورت خودکار bind میشوند و برعکس. اگر کاربر مقادیر آنها را تغییر دهد، تغییرات نهایی به صورت خودکار به خواص متناظری در کنترلر جاری منعکس خواهند شد (two-way data binding).
دو دکمه نیز تعریف شدهاند که به اکشنهای save و edit متصل هستند.
بنابراین نیاز به یک کنترلر جدید، به نام post داریم تا بتوان رفتار قالب post را کنترل کرد. برای این منظور فایل جدید Scripts\Controllers\post.js را با محتوای ذیل ایجاد کنید:
Blogger.PostController = Ember.ObjectController.extend({ isEditing: false, actions: { edit: function () { this.set('isEditing', true); }, save: function () { this.set('isEditing', false); } } });
<script src="Scripts/Controllers/post.js" type="text/javascript"></script>
اگر به کدهای این کنترلر دقت کرده باشید، اینبار زیرکلاسی از ObjectController ایجاد شدهاست و نه Controller، مانند مثالهای قبل. ObjectController تغییرات رخ داده بر روی خواص مدل را که توسط کنترلر در معرض دید قالب قرار دادهاست، به صورت خودکار به مدل مرتبط نیز منعکس میکند (Ember.ObjectController.extend)؛ اما Controller خیر (Ember.Controller.extend). در اینجا مدل کنترلر، تنها «یک» شیء است که بر اساس id آن انتخاب شدهاست. به همین جهت از ObjectController برای ارائه two-way data binding کمک گرفته شد.
در ember.js، یک قالب تنها با کنترلر خودش دارای تبادل اطلاعات است. اگر این کنترلر از نوع ObjectController باشد، تغییرات خاصیتی در یک قالب، ابتدا به کنترلر آن منعکس میشود و سپس این کنترلر، در صورت یافتن معادلی از این خاصیت در مدل، آنرا به روز خواهد کرد. در حالت استفاده از Controller معمولی، صرفا تبادل اطلاعات بین قالب و کنترلر را شاهد خواهیم بود و نه بیشتر.
در ابتدای کار مقدار خاصیت isEditing مساوی false است. این مورد سبب میشود تا در بار اول بارگذاری اطلاعات یک مطلب انتخابی، صرفا عنوان و محتوای مطلب نمایش داده شوند؛ به همراه یک دکمهی edit. با کلیک بر روی دکمهی edit، مطابق کدهای کنترلر فوق، تنها خاصیت isEditing به true تنظیم میشود و در این حالت، بدنهی اصلی شرط if isEditing در قالب post، رندر خواهد شد.
برای مثال در ابتدا مطلب شماره یک را انتخاب میکنیم:
با کلیک بر روی دکمهی edit، فرم ویرایش ظاهر خواهد شد:
نکتهی جالب آن، مقدار دهی خودکار المانهای ویرایش اطلاعات است. در این حالت سعی کنید، عنوان مطلب جاری را اندکی ویرایش کنید:
با ویرایش عنوان، میتوان بلافاصله مقدار تغییر یافته را در برچسب عنوان مطلب نیز مشاهده کرد. این مورد دقیقا مفهوم two-way data binding و اتصال مقادیر value هر کدام از handlebar helperهای ویژهی input و textarea را به عناصر مدل ارائه شده توسط کنترلر post، بیان میکند.
در این حالت در کدهای متد save، تنها کافی است که خاصیت isEditing را به false تنظیم کنیم. زیرا کلیه مقادیر ویرایش شده توسط کاربر، در همان لحظه در برنامه منتشر شدهاند و نیاز به کار بیشتری برای اعمال تغییرات نیست.
اضافه کردن دکمهی مرتب سازی بر اساس عناوین، در صفحهی اول سایت
Ember.ObjectController.extend برای data bindg یک شیء کاربرد دارد. اگر قصد داشته باشیم با آرایهای از اشیاء کار کنیم میتوان از ArrayController استفاده کرد. فرض کنید در صفحهی اول سایت میخواهیم امکان مرتب سازی مطالب را بر اساس عنوان آنها اضافه کنیم. فایل Scripts\Templates\posts.hbs را گشوده و لینک Sort by title را به انتهای آن اضافه کنید:
<h2>Emeber.js blog</h2> <ul> {{#each post in model}} <li>{{#link-to 'post' post.id}}{{post.title}}{{/link-to}}</li> {{/each}} </ul> <a href="#" class="btn btn-primary" {{action 'sortByTitle'}}>Sort by title</a>
Blogger.PostsController = Ember.ArrayController.extend({ sortProperties: ['id'],// مقادیر پیش فرض مرتب سازی sortAscending: false, actions: { sortByTitle: function () { this.set('sortProperties', ['title']); this.set('sortAscending', !this.get('sortAscending')); } } });
در ادامه، تعریف مدخل این کنترلر جدید را نیز باید به فایل index.html، اضافه کرد:
<script src="Scripts/Controllers/posts.js" type="text/javascript"></script>
اگر برنامه را در این حالت اجرا کرده و بر روی دکمهی Sort by title کلیک کنید، اتفاقی رخ نمیدهد. علت اینجا است که ArrayController خروجی تغییر یافته خودش را توسط خاصیتی به نام arrangedContent در اختیار قالب خود قرار میدهد. بنابراین نیاز است فایل قالب Scripts\Templates\posts.hbs را به نحو ذیل ویرایش کرد:
<h2>Emeber.js blog</h2> <ul> {{#each post in arrangedContent}} <li>{{#link-to 'post' post.id}}{{post.title}}{{/link-to}}</li> {{/each}} </ul> <a href="#" class="btn btn-primary" {{action 'sortByTitle'}}>Sort by title</a>
یک نکته: حلقهی ویژهای به نام each
اگر قالب Scripts\Templates\posts.hbs را به نحو ذیل، با یک حلقهی each ساده بازنویسی کنید:
<h2>Ember.js blog</h2> <ul> {{#each}} <li>{{#link-to 'post' id}}{{title}}{{/link-to}}</li> {{/each}} </ul> <a href="#" class="btn btn-primary" {{action 'sortByTitle'}}>Sort by title</a>
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید:
EmberJS03_03.zip
نقشهی راه فراگیری ES6
توسعه مستمر (Continuous deployment)
SQLite compiled to javascript
For the impatients, try the demo here: http://kripken.github.io/sql.js/examples/GUI
sql.js is a port of SQLite to Webassembly, by compiling the SQLite C code with Emscripten. It uses a virtual database file stored in memory, and thus doesn't persist the changes made to the database. However, it allows you to import any existing sqlite file, and to export the created database as a javascript typed array.