.... BirthDate = new DateTime(2008, 1, 28), <<< FirstVisit = new DateTime(2008,1,12), >>> AnimalType = dog, .....
مشکل حل شد.
.... BirthDate = new DateTime(2008, 1, 28), <<< FirstVisit = new DateTime(2008,1,12), >>> AnimalType = dog, .....
const express = require('express') const app = express() const PORT = 3000; app.get('/', (req, res) => { res.send('Hello World') }) app.listen(PORT, () => { console.log(`listening on port ${PORT}!`) })
"scripts": { "start": "node index" },
FROM node ENV NODE_ENV=production COPY . /var/www WORKDIR /var/www RUN npm i EXPOSE 3000 ENTRYPOINT npm start
docker build -f Dockerfile -t alikhll/testnode1 .
docker run -d -p 8080:3000 alikhll/testnode1
dotnet new web dotnet restore dotnet run
FROM microsoft/dotnet ENV ASPNETCORE_URLS http://*:3000 COPY . /var/www WORKDIR /var/www RUN dotnet restore EXPOSE 3000 ENTRYPOINT dotnet run
docker build -f Dockerfile -t alikhll/testasp1 .
docker run -d -p 8080:3000 alikhll/testasp1
docker login
docker push alikhll/testnode1
docker pull alikhll/testnode1
docker pull node
docker images
در ادامه نیاز داریم یک دایرکتوری را ایجاد کرده و فایل index.js را درون آن بسازیم:
mkdir testapp cd testapp touch index.js npm init npm i express --save
یک دایرکتوری را ساختیم و همچنین express را نیز نصب نمودیم.
اکنون package.json را باز کرده و این قسمت را جایگزین کنید؛ تا با استفاده از npm start، برنامه اجرا شود:
"scripts": { "start": "node index" }
index.js را باز کرده و کدهای زیر را وارد کنید:
const express = require('express') const app = express() const PORT = 3000; app.get('/', function (req, res) { res.send('Hello World') }) app.listen(PORT, function () { console.log(`listening on port ${PORT}!`) })
همه چیز خیلی ساده در نظر گرفته شده است. از فریمورک express استفاده کردیم و یک سرور را بر روی پورت 3000 اجرا کرده و همچنین بر روی آدرس "/" یک سطر کد Hello World اجرا میشود.
خوب، فرض کنید قصد داریم با استفاده از volume، کد فوق را بر روی container اجرا کنیم.
برای اجرا شدن این کدها بر روی Volume، دستور زیر را درون ترمینال خود وارد کنید:
docker run -d -p 3030:3000 -v $(pwd):/var/www -w "/var/www" node npm start
شرح دستور فوق:
دستور ساخت container با استفاده از ارگومان run
d- برای اجرا شدن container در حالت detached، باعث میشود اجرا شدن آن در حالت بکگراند بوده و بتوانید بر روی ترمینال مربوطه، دستورات دیگری را وارد نمایید.
p- پورت داخلی و خارجی را مشخص میکند. در اینجا پورت داخلی، 3000 و خارجی، 3030 میباشد.
آرگومان v- برای ساخت volume و (pwd)$ دایرکتوری جاری را بر روی سیستم شما، نشان خواهد داد. بعد از آن /var/www یک دایرکتوری فرضی است (هر آدرس دلخواهی را میتوانیم داشته باشیم) که قرار است بوسیلهیdocker ساخته شود و از آن اشارهگری به دایرکتوری جاری بر روی ماشین محلی زده شود.
w- همان WorkingDirectory میباشد. بدلیل اینکه میخواهیم بر روی container به دایرکتوری که کدهای ما وجود دارد، وارد شود.
بعد از آن اسم image ای را که قرار است از آن استفاده شود، آورده تا container ایجاد شود.
و بعد از npm start برنامه را اجرا خواهد کرد.
پس از اجرا کردن دستور فوق، container ایجاد میشود و قابلیت اجرایی دارد (با استفاده از ip و port خارجی بر روی browser میتوانیم برنامه را مشاهده کنیم).
حال با استفاده از دستور زیر لیست containerهای اجرایی را مشاهده خواهیم کرد:
docker ps
میبینید که container، اجرا شده و پورت آن مشخص شدهاست. تصویر و کلید هش شدهی منحصر به فرد آن را نیز مشاهده میفرمایید.
حتی میتوانید به راحتی درون container را با استفاده از دستور زیر مشاهده کنید:
docker exec -it 6003 bash
6003 ابتدای کلید container ایجاد شدهاست و با استفاده از bash وارد محیط command line در container ایجاد شده خواهیم شد و دسترسی کاملی خواهیم داشت.
بطور مثال برای دیدن کدهای index.js بر روی container ایجاد شده، بعد از دستور فوق، command زیر را وارد نمایید:
cat index.js
جالب است بدانید از آنجائیکه container از طریق volume به دایرکتوری محلی شما لینک شدهاست، به محض اینکه بر روی سیستم خود کدی را تغییر داده و دوباره دستور فوق را اجرا کنید، تغییرات را مشاهده خواهید کرد.
برای متوقف کردن container از دستور زیر باید استفاده کرد:
docker stop 6003
نکته: 6003 آی دی container است و برای اجرای مجدد آن docker start 6003
بعد از متوقف کردن container و اجرای دستور docker ps متوجه خواهید شد که دیگر Container در لیست containerهای باز نیست.
با استفاده از دستور زیر به لیست تمامی Containerها چه در حال اجرا و چه متوقف شده، دسترسی خواهیم داشت:
docker ps -a
برای حذف container نیز از دستور زیر استفاده میکنیم:
docker rm -v 6003
rm برای حذف container و همچنین v- برای حذف volume میباشد.
Redis | RDBMS |
Redis همه چیز را در حافظه اصلی ذخیره میکند. | RDBMS همه چیز را در حافظه ثانویه ذخیره میکند. |
در Redis بخاطر ذخیره سازی دادهها در حافظه اصلی، خواندن و نوشتن عملیات به شدت سریع میباشد. | در RDBMS بخاطر ذخیره سازی دادهها در حافظه ثانویه، خواندن و نوشتن عملیات کند است. |
حافظه اصلی از نظر size کوچکتر و از لحاظ قیمت نسبت به حافظه ثانویه گرانتر میباشد. Redis نمیتواند دادههای بزرگ یا binary data را ذخیره کند. | حافظه ثانویه از نظر size بزرگتر و از لحاظ قیمت نسبت به حافظه اصلی ارزانتر میباشد. RDBMS به آسانی میتواند با انواع فایلها کار کند. |
> ng test --help
<alert type="success">Alert success!</alert>
import { NO_ERRORS_SCHEMA } from '@angular/core'; describe('AppComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ declarations: [ AppComponent ], schemas: [NO_ERRORS_SCHEMA] }).compileComponents(); }));
گزینه | مخفف | توضیح |
code-coverage-- | cc- | تولید گزارش code coverage که به صورت پیش فرض خاموش است. |
colors-- | به صورت پیش فرض فعال است و سبب نمایش رنگهای قرمز و سبز، برای آزمونهای شکست خورده و یا موفق میشود. | |
single-run-- | sr- | اجرای یکبارهی آزمونهای واحد، بدون فعال سازی گزینهی مشاهدهی مداوم تغییرات که به صورت پیش فرض خاموش است. |
progress-- | نمایش جزئیات کامپایل و اجرای آزمونهای واحد که به صورت پیش فرض فعال است. | |
sourcemaps-- | sm- | تولید فایلهای سورسمپ که به صورت پیش فرض فعال است. |
watch-- | w- | بررسی مداوم تغییرات فایلها و اجرای آزمونهای واحد به صورت خودکار که به صورت پیش فرض فعال است. |
> ng test -sr > ng test -w false
> ng test --code-coverage
> ng test -sr -cc
>ng e2e --help
گزینه | مخفف | توضیح |
config-- | c- | به فایل کانفیگ آزمونهای e2e اشاره میکند که به صورت پیشفرض همان protractor.conf.js واقع در ریشهی پروژهاست. |
element-explorer-- | ee- | بررسی و دیباگ protractor از طریق خط فرمان |
serve-- | s- | کامپایل و توزیع برنامه بر روی پورتی اتفاقی (حالت پیش فرض آن true است) |
specs-- | sp- | پیش فرض آن بررسی تمام specهای موجود در پروژهاست. اگر نیاز به لغو آن باشد میتوان از این گزینه استفاده کرد. |
webdriver-update-- | wu- |
به روز رسانی web driver که به صورت پیش فرض فعال است. |
getParagraphText() { return element(by.css('app-root h1')).getText(); }
expect(page.getParagraphText()).toEqual('app works!');
Error: getaddrinfo ENOTFOUND chromedriver.storage.googleapis.com chromedriver.storage.googleapis.com:443
در حالیکه CoffeeScript برخی از نقاط ضعف جاوااسکریپت را اصلاح کرده است، اما همچنان معایب دیگری نیز وجود دارند، که شما تنها باید از این نقاط ضعف آگاه باشید. یکی از این موارد، تابع eval است. برای استفاده از آن، باید با اشکالاتی که در حین کار با آن مواجه میشوید، آگاهی کامل داشته باشید و در صورت امکان از استفاده از آن اجتناب کنید.
تابع eval یک رشته از کد جاوااسکریپت را در حوزهی محلی اجرا میکند و توابعی مانند setTimeout و setInterval نیز میتوانند در آرگومان اولشان یک رشته از کد جاوااسکریپت را دریافت و ارزیابی کنند.
با این حال، مانند eval ،with نیز ردیابی کامپایلر را از کار میاندازد و این امر تاثیر بسیار زیادی بر روی کارآیی آن دارد. کامپایلر هیچ ایده ای درباره کدی که درون eval قرار داده شده است، ندارد تا زمانی که آن را اجرا کند. به همین دلیل نمیتواند هیچ عمل بهینه سازی را بر روی انجام دهد. یکی دیگر از نگرانیهای استفادهی از eval، امنیت است. در صورتیکه شما ورودی را به eval ارسال کنید، eval باعث میشود که کد شما به راحتی در معرض حملات تزریق کد قرار میگیرد. در 99% از مواقع، وقتی شما میخواهید از eval استفاده کنید، راههای بهتر و امنتری وجود دارند ( مانند استفاده از براکت ).
# Don't do this model = eval(modelName) # Use square brackets instead model = window[modelName]
اپراتور typeof احتمالا بزرگترین نقص طراحی جاوااسکریپت است؛ تنها به این دلیل که اساسا به طور کامل شکست خورده است. در واقع از آن فقط یک استفاده میشود تا تشخیص داده شود که یک مقدار undefined است یا نه.
typeof undefinedVar is "undefined"
Value Class Type ---------------------------------------------- "foo" String string new String("foo") String object 1.2 Number number new Number(1.2) Number object true Boolean boolean new Boolean(true) Boolean object new Date() Date object new Error() Error object [1,2,3] Array object new Array(1, 2, 3) Array object new Function("") Function function /abc/g RegExp object new RegExp("meow") RegExp object {} Object object new Object() Object object
type = do -> classToType = {} for name in "Boolean Number String Function Array Date RegExp Undefined Null".split(" ") classToType["[object " + name + "]"] = name.toLowerCase() (obj) -> strType = Object::toString.call(obj) classToType[strType] or "object" # Returns the sort of types we'd expect: type("") # "string" type(new String) # "string" type([]) # "array" type(/\d/) # "regexp" type(new Date) # "date" type(true) # "boolean" type(null) # "null" type({}) # "object"
if typeof aVar isnt "undefined" objectType = type(aVar)
objectType = type(aVar?)
anArray?.push? aValue
کلمهی کلیدی instanceof نیز تقریبا همانند typeof شکست خورده است. در حالت ایده آل، instanceof، سازندهی دو شیء را با هم مقایسه میکند، در صورتیکه یک شیء نمونهای از شیء دیگر باشد، یک مقدار boolean را باز میگرداند. در واقع instanceof موقعی کار مقایسه را انجام میدهد که اشیاء، سفارشی سازی شده باشند. وقتی عمل مقایسه میخواهد بر روی این نوع اشیاء سفارشی سازی شده، انجام شود، استفاده از typeof بیفایده است.
new String("foo") instanceof String # true "foo" instanceof String # false
class Parent class Child extends Parent child = new Child child instanceof Child # true child instanceof Parent # true
anObject = {one: 1, two: 2} delete anObject.one anObject.hasOwnProperty("one") # false
aVar = 1 delete aVar typeof Var # "integer"
aVar = 1 aVar = null
برای آنکه طراحی قوی و درست را یاد بگیریم، لازم است که نشانههای طراحی ضعیف را بدانیم. این نشانهها عبارتند از:
۱- Rigidity (انعطاف ناپذیری): یک ماژول انعطاف ناپذیر است، اگر یک تغییر در آن، منجر به تغییرات در سایر ماژولها گردد. هر چه میزان تغییرات آبشاری بیشتر باشد، نرم افزار خشکتر و غیر منعطفتر است.
۲- Fragility (شکنندگی): وقتی که تغییر در قسمتی از نرم افزار باعث به بروز اشکال در بخشهای دیگر شود.
۳- Immobility (تحرک ناپذیری): وقتی نتوان قسمت هایی از نرم افزار را در جاهای دیگر استفاده نمود و یا به کار گیری آن هزینه و ریسک بالایی داشته باشد.
۴- Viscosity (لزجی): وقتی حفظ طراحی اصولی پروژه مشکل باشد، میگوییم پروژه لزج شده است. به عنوان مثال وقتی تغییری در پروژه به دو صورت اصولی و غیر اصولی قابل انجام باشد و روش غیر اصولی راحتتر باشد، میگوییم لزج شده است. البته لزجی محیط هم وجود دارد مثلا انجام کار به صورت اصولی نیاز به Build کل پروژه دارد که زیاد طول میکشد.
۵- Needless Complexity (پیچیدگی اضافی): زمانی که امکانات بدون استفاده در نرم افزار قرار گیرند.
۶- Needless Repetition (تکرارهای اضافی): وقتی که کدهایی با منطق یکسان در جاهای مختلف برنامه کپی میشوند، این مشکلات رخ میدهند.
۷- Opacity (ابهام): وقتی که فهمیدن یک ماژول سخت شود، رخ میدهد و کد برنامه مبهم بوده و قابل فهم نباشد.
در روشهای غیر چابک یکی از دلایل اصلی پوسیدگی، عدم تطابق نرم افزار با تغییرات درخواستی است. لازم است که این تغییرات به سرعت انجام شوند و ممکن است که توسعه دهندگان از طراحی ابتدایی اطلاعی نداشته باشند. با این حال ممکن است تغییرایی قابل انجام باشد ولی برخی از آنها طراحی اصلی را نقض میکنند. ما نباید تغییرات نیازمندیها را مقصر بدانیم. باید طراحی ما قابلیت تطبیق با تغییرات را داشته باشد.
یک تیم چابک از تغییرات استقبال میکند. وقت بسیار کمی را روی طراحی اولیه کل کار میگذارد و سعی میکند که طراحی سیستم را تا جایی که ممکن است ساده و تمیز نگه دارد با استفاده از تستهای واحد و یکپارچه از آن محافظت کند. این طراحی را انعطاف پذیر میکند. تیم از قابلیت انعطاف پذیری برای بهبود همیشگی طراحی استفاده میکند. بنابراین در هر تکرار نرم افزاری خواهیم داشت که نیازمندیهای آن تکرار را برآورده میکند.
سناریویی را فرض کنید که در آن برای انجام عملیات CRUD از یک سرویس Web API استفاده میشود. همچنین مدیریت دادهها با مدل Code-First پیاده سازی شده است. در مثال جاری یک کلاینت Console Application خواهیم داشت که یک سرویس Web API را فراخوانی میکند. توجه داشته باشید که هر اپلیکیشن در Solution مجزایی قرار دارد. تفکیک پروژهها برای شبیه سازی یک محیط n-Tier انجام شده است.
فرض کنید مدلی مانند تصویر زیر داریم.
همانطور که میبینید مدل جاری، سفارشات یک اپلیکیشن فرضی را معرفی میکند. میخواهیم مدل و کد دسترسی به دادهها را در یک سرویس Web API پیاده سازی کنیم، تا هر کلاینتی که از HTTP استفاده میکند بتواند عملیات CRUD را انجام دهد. برای ساختن سرویس مورد نظر مراحل زیر را دنبال کنید.
public class Order { public int OrderId { get; set; } public string Product { get; set; } public int Quantity { get; set; } public string Status { get; set; } public byte[] TimeStamp { get; set; } }
public class Recipe1Context : DbContext { public Recipe1Context() : base("Recipe1ConnectionString") { } public DbSet<Order> Orders { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Entity<Order>().ToTable("Orders"); // Following configuration enables timestamp to be concurrency token modelBuilder.Entity<Order>().Property(x => x.TimeStamp) .IsConcurrencyToken() .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Computed); } }
<connectionStrings> <add name="Recipe1ConnectionString" connectionString="Data Source=.; Initial Catalog=EFRecipes; Integrated Security=True; MultipleActiveResultSets=True" providerName="System.Data.SqlClient" /> </connectionStrings>
protected void Application_Start() { // Disable Entity Framework Model Compatibilty Database.SetInitializer<Recipe1Context>(null); ... }
public class OrderController : ApiController { // GET api/order public IEnumerable<Order> Get() { using (var context = new Recipe1Context()) { return context.Orders.ToList(); } } // GET api/order/5 public Order Get(int id) { using (var context = new Recipe1Context()) { return context.Orders.FirstOrDefault(x => x.OrderId == id); } } // POST api/order public HttpResponseMessage Post(Order order) { // Cleanup data from previous requests Cleanup(); using (var context = new Recipe1Context()) { context.Orders.Add(order); context.SaveChanges(); // create HttpResponseMessage to wrap result, assigning Http Status code of 201, // which informs client that resource created successfully var response = Request.CreateResponse(HttpStatusCode.Created, order); // add location of newly-created resource to response header response.Headers.Location = new Uri(Url.Link("DefaultApi", new { id = order.OrderId })); return response; } } // PUT api/order/5 public HttpResponseMessage Put(Order order) { using (var context = new Recipe1Context()) { context.Entry(order).State = EntityState.Modified; context.SaveChanges(); // return Http Status code of 200, informing client that resouce updated successfully return Request.CreateResponse(HttpStatusCode.OK, order); } } // DELETE api/order/5 public HttpResponseMessage Delete(int id) { using (var context = new Recipe1Context()) { var order = context.Orders.FirstOrDefault(x => x.OrderId == id); context.Orders.Remove(order); context.SaveChanges(); // Return Http Status code of 200, informing client that resouce removed successfully return Request.CreateResponse(HttpStatusCode.OK); } } private void Cleanup() { using (var context = new Recipe1Context()) { context.Database.ExecuteSqlCommand("delete from [orders]"); } } }
نکته: قسمت هایی از اپلیکیشن که باید در لایههای مختلف مورد استفاده قرار گیرند - مانند کلاسهای موجودیتها - بهتر است در لایه مجزایی قرار داده شده و به اشتراک گذاشته شوند. مثلا میتوانید پروژه ای از نوع Class Library بسازید و تمام موجودیتها را در آن تعریف کنید. سپس لایههای مختلف این پروژه را ارجاع خواهند کرد.
فایل program.cs را باز کنید و کد زیر را به آن اضافه نمایید.
private HttpClient _client; private Order _order; private static void Main() { Task t = Run(); t.Wait(); Console.WriteLine("\nPress <enter> to continue..."); Console.ReadLine(); } private static async Task Run() { // create instance of the program class var program = new Program(); program.ServiceSetup(); program.CreateOrder(); // do not proceed until order is added await program.PostOrderAsync(); program.ChangeOrder(); // do not proceed until order is changed await program.PutOrderAsync(); // do not proceed until order is removed await program.RemoveOrderAsync(); } private void ServiceSetup() { // map URL for Web API cal _client = new HttpClient { BaseAddress = new Uri("http://localhost:3237/") }; // add Accept Header to request Web API content // negotiation to return resource in JSON format _client.DefaultRequestHeaders.Accept. Add(new MediaTypeWithQualityHeaderValue("application/json")); } private void CreateOrder() { // Create new order _order = new Order { Product = "Camping Tent", Quantity = 3, Status = "Received" }; } private async Task PostOrderAsync() { // leverage Web API client side API to call service var response = await _client.PostAsJsonAsync("api/order", _order); Uri newOrderUri; if (response.IsSuccessStatusCode) { // Capture Uri of new resource newOrderUri = response.Headers.Location; // capture newly-created order returned from service, // which will now include the database-generated Id value _order = await response.Content.ReadAsAsync<Order>(); Console.WriteLine("Successfully created order. Here is URL to new resource: {0}", newOrderUri); } else Console.WriteLine("{0} ({1})", (int)response.StatusCode, response.ReasonPhrase); } private void ChangeOrder() { // update order _order.Quantity = 10; } private async Task PutOrderAsync() { // construct call to generate HttpPut verb and dispatch // to corresponding Put method in the Web API Service var response = await _client.PutAsJsonAsync("api/order", _order); if (response.IsSuccessStatusCode) { // capture updated order returned from service, which will include new quanity _order = await response.Content.ReadAsAsync<Order>(); Console.WriteLine("Successfully updated order: {0}", response.StatusCode); } else Console.WriteLine("{0} ({1})", (int)response.StatusCode, response.ReasonPhrase); } private async Task RemoveOrderAsync() { // remove order var uri = "api/order/" + _order.OrderId; var response = await _client.DeleteAsync(uri); if (response.IsSuccessStatusCode) Console.WriteLine("Sucessfully deleted order: {0}", response.StatusCode); else Console.WriteLine("{0} ({1})", (int)response.StatusCode, response.ReasonPhrase); }
با اجرای اپلیکیشن Web API شروع کنید. این اپلیکیشن یک کنترلر Web API دارد که پس از اجرا شما را به صفحه خانه هدایت میکند. در این مرحله اپلیکیشن در حال اجرا است و سرویسهای ما قابل دسترسی هستند.
حال اپلیکیشن کنسول را باز کنید. روی خط اول کد program.cs یک breakpoint تعریف کرده و اپلیکیشن را اجرا کنید. ابتدا آدرس سرویس Web API را پیکربندی کرده و خاصیت Accept Header را مقدار دهی میکنیم. با این کار از سرویس مورد نظر درخواست میکنیم که دادهها را با فرمت JSON بازگرداند. سپس یک آبجکت Order میسازیم و با فراخوانی متد PostAsJsonAsync آن را به سرویس ارسال میکنیم. این متد روی آبجکت HttpClient تعریف شده است. اگر به اکشن متد Post در کنترلر Order یک breakpoint اضافه کنید، خواهید دید که این متد سفارش جدید را بعنوان یک پارامتر دریافت میکند و آن را به لیست موجودیتها در Context جاری اضافه مینماید. این عمل باعث میشود که آبجکت جدید بعنوان Added علامت گذاری شود، در این مرحله Context جاری شروع به ردیابی تغییرات میکند. در آخر با فراخوانی متد SaveChanges دادهها را ذخیره میکنیم. در قدم بعدی کد وضعیت 201 (Created) و آدرس منبع جدید را در یک آبجکت HttpResponseMessage قرار میدهیم و به کلاینت ارسال میکنیم. هنگام استفاده از Web API باید اطمینان حاصل کنیم که کلاینتها درخواستهای ایجاد رکورد جدید را بصورت POST ارسال میکنند. درخواستهای HTTP Post بصورت خودکار به اکشن متد متناظر نگاشت میشوند.
در مرحله بعد عملیات بعدی را اجرا میکنیم، تعداد سفارش را تغییر میدهیم و موجودیت جاری را با فراخوانی متد PutAsJsonAsync به سرویس Web API ارسال میکنیم. اگر به اکشن متد Put در کنترلر سرویس یک breakpoint اضافه کنید، خواهید دید که آبجکت سفارش بصورت یک پارامتر دریافت میشود. سپس با فراخوانی متد Entry و پاس دادن موجودیت جاری بعنوان رفرنس، خاصیت State را به Modified تغییر میدهیم، که این کار موجودیت را به Context جاری میچسباند. حال فراخوانی متد SaveChanges یک اسکریپت بروز رسانی تولید خواهد کرد. در مثال جاری تمام فیلدهای آبجکت Order را بروز رسانی میکنیم. در شمارههای بعدی این سری از مقالات، خواهیم دید چگونه میتوان تنها فیلدهایی را بروز رسانی کرد که تغییر کرده اند. در آخر عملیات را با بازگرداندن کد وضعیت 200 (OK) به اتمام میرسانیم.
در مرحله بعد، عملیات نهایی را اجرا میکنیم که موجودیت Order را از منبع داده حذف میکند. برای اینکار شناسه (Id) رکورد مورد نظر را به آدرس سرویس اضافه میکنیم و متد DeleteAsync را فراخوانی میکنیم. در سرویس Web API رکورد مورد نظر را از دیتابیس دریافت کرده و متد Remove را روی Context جاری فراخوانی میکنیم. این کار موجودیت مورد نظر را بعنوان Deleted علامت گذاری میکند. فراخوانی متد SaveChanges یک اسکریپت Delete تولید خواهد کرد که نهایتا منجر به حذف شدن رکورد میشود.
در یک اپلیکیشن واقعی بهتر است کد دسترسی دادهها از سرویس Web API تفکیک شود و در لایه مجزایی قرار گیرد.
Banner | این
قسمت که عموما برای اجزای مهمی مثل هدر سایت قرار میگیرد و شامل معرفی وب
سایت هست و در همهی صفحات وجود دارد که شامل لوگو، اطلاعات عمومی سایت و
اسپانسرها و ... میگردد و بسیار مهم است که تنها یکبار در صفحهی وب به کار
برود و تکرار آن پرهیز شود. |
Main | این
نقش به محتوای اصلی وب سایت اشاره میکند و نباید بیشتر از یکبار در هر صفحهی وب به کار برود و عموما بهتر است این خصوصیت در تگ div قرار گیرد:<div Role="main"></div> <main role="main">..... |
Navigation | اشاره به یک ناحیه پر از المانهای لینک برای ارتباط با صفحات دیگر |
Complementary | مشخص سازی ناحیهای که اطلاعات اضافی دربارهی محتوای اصلی سایت دارد؛ مانند بخش مقالات مرتبط، آخرین کامنتها و ... |
ContentInfo | این نقش که بیشتر برای فوتر مناسب است برای محتوایی به کار میرود که در آن به قوانین کپی رایت و ... اشاره میشود. |
form | برای اشاره به فرمها که دارای قسمتهای ورودی کاربر هستند. |
search | در صورتیکه فرمی دارید و از آن برای گزینهی جست و جو استفاده میکنید، از این نقش استفاده کنید. |
application | برای
اینکه وب سایت خود را به صورت یک وب اپلیکیشن معرفی کنید؛ تا یک صفحه وب معمولی،
استفاده میشود و برای وب سایتهای قدیمی یا با حالت سنتی توصیه نمیشود و
به برنامههای کمکیار معلولین میگوند که از حالت عادی به حالت
application سوئیچ کنند؛ پس با دقت بیشتری باید از این گزینه استفاده کرد. |
<div id="some-id" class="some-class" aria-live="assertive"><div>
<div role="main" aria-labelledby="some-id"> <h1 id="some-id">This Is A Heading</h1> Main content... </div>
<div id="billing">Billing Address</div> <div> <div id="name">Name</div> <input type="text" aria-labelledby="name billing"/> </div> <div> <div id="address">Address</div> <input type="text" aria-labelledby="address billing"/> </div>
<div id="radio_label">My radio label</div> <ul role="radiogroup" aria-labelledby="radio_label"> <li role="radio">Item #1</li> <li role="radio">Item #2</li> <li role="radio">Item #3</li> </ul>
<form></form> <form role="form"></form>