C# Language Specification 4.0
این کتابهایی که نام بردید تشکیل شده از تمام مؤلفههای دات نت. مثلا اون کتاب 1852 صفحهای از WCF تا WPF را هم دارد. خود WPF جامع حدود 800 صفحه است. یا WCF راحت 600 صفحه است.
Relative URl | HTTP method | Action |
api/products/ | GET | گرفتن لیست تمام محصولات |
api/products/id/ | GET | گرفتن یک محصول بر اساس شناسه |
api/products?category=category/ | GET | گرفتن یک محصول بر اساس طبقه بندی |
api/products/ | POST | ایجاد یک محصول جدید |
api/products/id/ | PUT | بروز رسانی یک محصول |
api/products/id/ | DELETE | حذف یک محصول |
همانطور که مشاهده میکنید برخی از آدرس ها، شامل شناسه محصول هم میشوند. بعنوان مثال برای گرفتن محصولی با شناسه 28، کلاینت یک درخواست GET را به آدرس زیر ارسال میکند:
http://hostname/api/products/28
سرویس ما آدرس هایی برای دستیابی به دو نوع منبع (resource) را تعریف میکند:
URI | Resource |
api/products/ | لیست تمام محصولات |
api/products/id/ | یک محصول مشخص |
چهار متد اصلی HTTP یعنی همان GET, PUT, POST, DELETE میتوانند بصورت زیر به عملیات CRUD نگاشت شوند:
نکته: متد PUT موجودیت محصول (product entity) را کاملا جایگزین میکند. به بیان دیگر، از کلاینت انتظار میرود که آبجکت کامل محصول را برای بروز رسانی ارسال کند. اگر میخواهید از بروز رسانیهای جزئی/پاره ای (partial) پشتیبانی کنید متد PATCH توصیه میشود. مثال جاری متد PATCH را پیاده سازی نمیکند.
ویژوال استودیو را باز کنید و پروژه جدیدی از نوع ASP.NET MVC Web Application بسازید. نام پروژه را به "ProductStore" تغییر دهید و OK کنید.
در دیالوگ New ASP.NET Project قالب Web API را انتخاب کرده و تایید کنید.
یک مدل، آبجکتی است که داده اپلیکیشن شما را نمایندگی میکند. در ASP.NET Web API میتوانید از آبجکتهای Strongly-typed بعنوان مدل هایتان استفاده کنید که بصورت خودکار برای کلاینت به فرمتهای JSON, XML مرتب (Serialize) میشوند. در مثال جاری، دادههای ما محصولات هستند. پس کلاس جدیدی بنام Product میسازیم.
در پوشه Models کلاس جدیدی با نام Product بسازید.
حال خواص زیر را به این کلاس اضافه کنید.
namespace ProductStore.Models { public class Product { public int Id { get; set; } public string Name { get; set; } public string Category { get; set; } public decimal Price { get; set; } } }
ما نیاز به ذخیره کردن کلکسیونی از محصولات داریم، و بهتر است این کلکسیون از پیاده سازی سرویس تفکیک شود. در این صورت بدون نیاز به بازنویسی کلاس سرویس میتوانیم منبع دادهها را تغییر دهیم. این نوع طراحی با نام الگوی مخزن یا Repository Pattern شناخته میشود. برای شروع نیاز به یک قرارداد جنریک برای مخزنها داریم.
روی پوشه Models کلیک راست کنید و گزینه Add, New Item را انتخاب نمایید.
نوع آیتم جدید را Interface انتخاب کنید و نام آن را به IProductRepository تغییر دهید.
حال کد زیر را به این اینترفیس اضافه کنید.
namespace ProductStore.Models { public interface IProductRepository { IEnumerable<Product> GetAll(); Product Get(int id); Product Add(Product item); void Remove(int id); bool Update(Product item); } }
namespace ProductStore.Models { public class ProductRepository : IProductRepository { private List<Product> products = new List<Product>(); private int _nextId = 1; public ProductRepository() { Add(new Product { Name = "Tomato soup", Category = "Groceries", Price = 1.39M }); Add(new Product { Name = "Yo-yo", Category = "Toys", Price = 3.75M }); Add(new Product { Name = "Hammer", Category = "Hardware", Price = 16.99M }); } public IEnumerable<Product> GetAll() { return products; } public Product Get(int id) { return products.Find(p => p.Id == id); } public Product Add(Product item) { if (item == null) { throw new ArgumentNullException("item"); } item.Id = _nextId++; products.Add(item); return item; } public void Remove(int id) { products.RemoveAll(p => p.Id == id); } public bool Update(Product item) { if (item == null) { throw new ArgumentNullException("item"); } int index = products.FindIndex(p => p.Id == item.Id); if (index == -1) { return false; } products.RemoveAt(index); products.Add(item); return true; } } }
اگر قبلا با ASP.NET MVC کار کرده باشید، با مفهوم کنترلرها آشنایی دارید. در ASP.NET Web API کنترلرها کلاس هایی هستند که درخواستهای HTTP دریافتی از کلاینت را به اکشن متدها نگاشت میکنند. ویژوال استودیو هنگام ساختن پروژه شما دو کنترلر به آن اضافه کرده است. برای مشاهد آنها پوشه Controllers را باز کنید.
کنترلر ValuesController را حذف کنید، نیازی به این آیتم نخواهیم داشت. حال برای اضافه کردن کنترلری جدید مراحل زیر را دنبال کنید.
در پنجره Solution Explorer روی پوشه Controllers کلیک راست کرده و گزینه Add, Controller را انتخاب کنید.
در دیالوگ Add Controller نام کنترلر را به ProductsController تغییر داده و در قسمت Scaffolding Options گزینه Empty API Controller را انتخاب کنید.
حال فایل کنترلر جدید را باز کنید و عبارت زیر را به بالای آن اضافه نمایید.
using ProductStore.Models;
public class ProductsController : ApiController { static readonly IProductRepository repository = new ProductRepository(); }
ProductStore API اکشنهای متعددی در قالب متدهای HTTP GET در دسترس قرار میدهد. هر اکشن به متدی در کلاس ProductsController مرتبط است.
Relative URl | HTTP Method | Action |
api/products/ | GET | دریافت لیست تمام محصولات |
api/products/id/ | GET | دریافت محصولی مشخص بر اساس شناسه |
api/products?category=category/ | GET | دریافت محصولات بر اساس طبقه بندی |
برای دریافت لیست تمام محصولات متد زیر را به کلاس ProductsController اضافه کنید.
public class ProductsController : ApiController { public IEnumerable<Product> GetAllProducts() { return repository.GetAll(); } // .... }
public Product GetProduct(int id) { Product item = repository.Get(id); if (item == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } return item; }
public IEnumerable<Product> GetProductsByCategory(string category) { return repository.GetAll().Where( p => string.Equals(p.Category, category, StringComparison.OrdinalIgnoreCase)); }
قدم بعدی افزودن متدی به ProductsController برای ایجاد یک محصول جدید است. لیست زیر پیاده سازی ساده ای از این متد را نشان میدهد.
// Not the final implementation! public Product PostProduct(Product item) { item = repository.Add(item); return item; }
پیاده سازی فعلی این متد کار میکند، اما هنوز کامل نیست. در حالت ایده آل ما میخواهیم پیام HTTP Response موارد زیر را هم در بر گیرد:
ASP.NET Web API دستکاری پیام HTTP response را آسان میکند. لیست زیر پیاده سازی بهتری از این متد را نشان میدهد.
public HttpResponseMessage PostProduct(Product item) { item = repository.Add(item); var response = Request.CreateResponse<Product>(HttpStatusCode.Created, item); string uri = Url.Link("DefaultApi", new { id = item.Id }); response.Headers.Location = new Uri(uri); return response; }
بروز رسانی یک محصول با PUT ساده است.
public void PutProduct(int id, Product product) { product.Id = id; if (!repository.Update(product)) { throw new HttpResponseException(HttpStatusCode.NotFound); } }
برای حذف یک محصول متد زیر را به کلاس ProductsController اضافه کنید.
public void DeleteProduct(int id) { Product item = repository.Get(id); if (item == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } repository.Remove(id); }
Please open an issue in the library repository to alert its author and ask them to package the library using the Angular Package Format (https://goo.gl/jB3GVv).
ng new my-lib-test
ng generate application my-app-name
ng generate library my-lib
/* * Public API Surface of my-lib */ export * from './lib/my-lib.service'; export * from './lib/my-lib.component'; export * from './lib/my-lib.module';
export * from './lib/my-lib.models';
import { Injectable } from '@angular/core'; @Injectable({ providedIn: 'root' }) export class MyLibService { constructor() { } }
ng generate component show-data --project=my-lib
@NgModule({ imports: [ CommonModule, HttpClientModule ], declarations: [MyLibComponent, ShowDataComponent], exports: [MyLibComponent, ShowDataComponent] }) export class MyLibModule { }
ng build my-lib
Building Angular Package Building entry point 'my-lib' Rendering Stylesheets Rendering Templates Compiling TypeScript sources through ngc Downleveling ESM2015 sources through tsc Bundling to FESM2015 Bundling to FESM5 Bundling to UMD Minifying UMD bundle Remap source maps Relocating source maps Copying declaration files Writing package metadata Removing scripts section in package.json as it's considered a potential security vulnerability. Built my-lib Built Angular Package! - from: D:\my-lib-test\projects\my-lib - to: D:\my-lib-test\dist\my-lib
import { MyLibModule } from "my-lib"; @NgModule({ imports: [ BrowserModule, MyLibModule ] }) export class AppModule { }
{ "compilerOptions": { "paths": { "my-lib": [ "dist/my-lib" ] } } }
برای نمونه اگر شارهگر ماوس را بر روی my-lib قرار دهید، به درستی مسیر خوانده شدن آن، تشخیص داده میشود.
<lib-show-data></lib-show-data>
ng build my-lib --prod cd dist/my-lib npm publish
[AddInContract]
public interface ITranslator : IContract
{
string Translate(string input);
}
public abstract class TranslatorHostView
{
public abstract string Translate(string input);
}
[AddInBase]
public abstract class TranslatorHostView
{
public abstract string Translate(string input);
}
[HostAdapter]
public class TranslatorHostViewToContract : TranslatorHostView
{
ITranslator _contract;
ContractHandle _lifetime;
public TranslatorHostViewToContract(ITranslator contract)
{
_contract = contract;
_lifetime = new ContractHandle(contract);
}
public override string Translate (string inp)
{
return _contract.Translate(inp);
}
}
[AddInAdapter]
public class TranslatorAddInViewToContract : ContractBase, ITranslator
{
TranslatorAddInView _view;
public TranslatorAddInViewToContract(TranslatorView view)
{
_view = view;
}
public string Translate(string inp)
{
return _view.Translate(inp);
}
}
[AddIn("GoogleTranslator", Description="Universal translator",
Version="1.0.0.0", Publisher="YourName")]
public class GoogleAddIn : TranslatorAddInView
{
public string Translate(string input)
{
...
}
}
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net7.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> </PropertyGroup> <ItemGroup> <PackageReference Include="BenchmarkDotNet" Version="0.13.4" /> </ItemGroup> </Project>
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Jobs; namespace NET7Loops; [SimpleJob(RuntimeMoniker.Net60)] [SimpleJob(RuntimeMoniker.Net70)] [MemoryDiagnoser(false)] public class Benchmarks { private int[] ItemsArray; private List<int> ItemsList; [GlobalSetup] public void Setup() { var random = new Random(420); var randomItems = Enumerable.Range(0, 1000).Select(_ => random.Next()); ItemsArray = randomItems.ToArray(); ItemsList = randomItems.ToList(); } [Benchmark] public void For_Array() { for (var i = 0; i < ItemsArray.Length; i++) { var item = ItemsArray[i]; } } [Benchmark] public void For_List() { for (var i = 0; i < ItemsList.Count; i++) { var item = ItemsList[i]; } } [Benchmark] public void ForEach_Array() { foreach (var item in ItemsArray) { } } [Benchmark] public void ForEach_List() { foreach (var item in ItemsList) { } } }
using BenchmarkDotNet.Running; using NET7Loops; BenchmarkRunner.Run<Benchmarks>();
// Decompiled with JetBrains decompiler // Type: NET7Loops.Benchmarks // Assembly: NET7Loops, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null // MVID: E398BEE7-8123-4C55-AF9A-F7D83DDA73F1 // Assembly location: C:\Prog\1401\Net7Tests\NET7Loops\bin\Debug\net7.0\NET7Loops.dll // Compiler-generated code is shown using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Jobs; using System; using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; namespace NET7Loops { [NullableContext(1)] [Nullable(0)] [SimpleJob(RuntimeMoniker.Net60, -1, -1, -1, -1, null, false)] [SimpleJob(RuntimeMoniker.Net70, -1, -1, -1, -1, null, false)] [MemoryDiagnoser(false)] public class Benchmarks { private int[] ItemsArray; private List<int> ItemsList; [GlobalSetup] public void Setup() { Benchmarks.<>c__DisplayClass2_0 cDisplayClass20 = new Benchmarks.<>c__DisplayClass2_0(); cDisplayClass20.random = new Random(420); IEnumerable<int> source = Enumerable.Range(0, 1000).Select<int, int>(new Func<int, int>((object) cDisplayClass20, __methodptr(<Setup>b__0))); this.ItemsArray = source.ToArray<int>(); this.ItemsList = source.ToList<int>(); } [Benchmark(23, "C:\\Prog\\1401\\Net7Tests\\NET7Loops\\Benchmarks.cs")] public void For_Array() { for (int index = 0; index < this.ItemsArray.Length; ++index) { int items = this.ItemsArray[index]; } } [Benchmark(32, "C:\\Prog\\1401\\Net7Tests\\NET7Loops\\Benchmarks.cs")] public void For_List() { for (int index = 0; index < this.ItemsList.Count; ++index) { int items = this.ItemsList[index]; } } [Benchmark(41, "C:\\Prog\\1401\\Net7Tests\\NET7Loops\\Benchmarks.cs")] public void ForEach_Array() { int[] itemsArray = this.ItemsArray; for (int index = 0; index < itemsArray.Length; ++index) { int num = itemsArray[index]; } } [Benchmark(49, "C:\\Prog\\1401\\Net7Tests\\NET7Loops\\Benchmarks.cs")] public void ForEach_List() { List<int>.Enumerator enumerator = this.ItemsList.GetEnumerator(); try { while (enumerator.MoveNext()) { int current = enumerator.Current; } } finally { enumerator.Dispose(); } } public Benchmarks() { base..ctor(); } [CompilerGenerated] private sealed class <>c__DisplayClass2_0 { [Nullable(0)] public Random random; public <>c__DisplayClass2_0() { base..ctor(); } internal int <Setup>b__0(int _) { return this.random.Next(); } } } }
for (int index = 0; index < this.ItemsArray.Length; ++index) { int items = this.ItemsArray[index]; }
List<int>.Enumerator enumerator = this.ItemsList.GetEnumerator(); try { while (enumerator.MoveNext()) { int current = enumerator.Current; } } finally { enumerator.Dispose(); }
CREATE TABLE xmlInvoice ( invoiceId INT IDENTITY PRIMARY KEY, invoice XML )
INSERT INTO xmlInvoice VALUES(' <Invoice InvoiceId="1000" dept="hardware"> <CustomerName>Vahid</CustomerName> <LineItems> <LineItem><Description>Gear</Description><Price>9.5</Price></LineItem> </LineItems> </Invoice> ') INSERT INTO xmlInvoice VALUES(' <Invoice InvoiceId="1002" dept="garden"> <CustomerName>Mehdi</CustomerName> <LineItems> <LineItem><Description>Shovel</Description><Price>19.2</Price></LineItem> </LineItems> </Invoice> ') INSERT INTO xmlInvoice VALUES(' <Invoice InvoiceId="1003" dept="garden"> <CustomerName>Mohsen</CustomerName> <LineItems> <LineItem><Description>Trellis</Description><Price>8.5</Price></LineItem> </LineItems> </Invoice> ') INSERT INTO xmlInvoice VALUES(' <Invoice InvoiceId="1004" dept="hardware"> <CustomerName>Hamid</CustomerName> <LineItems> <LineItem><Description>Pen</Description><Price>1.5</Price></LineItem> </LineItems> </Invoice> ') INSERT INTO xmlInvoice VALUES(' <Invoice InvoiceId="1005" dept="IT"> <CustomerName>Ali</CustomerName> <LineItems> <LineItem><Description>Book</Description><Price>3.2</Price></LineItem> </LineItems> </Invoice> ') INSERT INTO xmlInvoice VALUES(' <Invoice InvoiceId="1006" dept="hardware"> <CustomerName>Reza</CustomerName> <LineItems> <LineItem><Description>M.Board</Description><Price>19.5</Price></LineItem> </LineItems> </Invoice> ')
CREATE XML SCHEMA COLLECTION invoice_xsd AS ' <xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="Invoice"> <xs:complexType> <xs:sequence> <xs:element name="CustomerName" type="xs:string" /> <xs:element name="LineItems"> <xs:complexType> <xs:sequence> <xs:element name="LineItem"> <xs:complexType> <xs:sequence> <xs:element name="Description" type="xs:string" /> <xs:element name="Price" type="xs:decimal" /> </xs:sequence> </xs:complexType> </xs:element> </xs:sequence> </xs:complexType> </xs:element> </xs:sequence> <xs:attribute name="InvoiceId" type="xs:unsignedShort" use="required" /> <xs:attribute name="dept" type="xs:string" use="required" /> </xs:complexType> </xs:element> </xs:schema>' Go CREATE TABLE xmlInvoice2 ( invoiceId INT IDENTITY PRIMARY KEY, invoice XML(document invoice_xsd) ) Go
UPDATE STATISTICS xmlInvoice DBCC FREEPROCCACHE
-- query 1 SELECT * FROM xmlInvoice WHERE invoice.exist('/Invoice[@InvoiceId = "1003"]') = 1 -- query 2 SELECT * FROM xmlInvoice WHERE invoice.exist('/Invoice/@InvoiceId[. = "1003"]') = 1 -- query 3 SELECT * FROM xmlInvoice WHERE invoice.exist('/Invoice[1]/@InvoiceId[. = "1003"]') = 1 -- query 4 SELECT * FROM xmlInvoice WHERE invoice.exist('(/Invoice/@InvoiceId)[1][. = "1003"]') = 1 -- query 5 SELECT * FROM xmlInvoice WHERE invoice.exist('/Invoice[CustomerName = "Vahid"]') = 1 -- query 6 SELECT * FROM xmlInvoice WHERE invoice.exist('/Invoice/CustomerName [.= "Vahid"]') = 1 -- query 7 SELECT * FROM xmlInvoice WHERE invoice.exist('/Invoice/LineItems/LineItem[Description = "Trellis"]') = 1 -- query 8 SELECT * FROM xmlInvoice WHERE invoice.exist('/Invoice/LineItems/LineItem/Description [.= "Trellis"]') = 1 -- query 9 SELECT * FROM xmlInvoice WHERE invoice.exist(' for $x in /Invoice/@InvoiceId where $x = 1003 return $x ') = 1 -- query 10 SELECT * FROM xmlInvoice WHERE invoice.value('(/Invoice/@InvoiceId)[1]', 'VARCHAR(10)') = '1003' -- یکبار هم با جدول شماره 2 که اسکیما دارد تمام این موارد تکرار شود UPDATE STATISTICS xmlInvoice DBCC FREEPROCCACHE GO