dotnet tool update -g docfx
docfx docs/docfx.json --serve
- run: dotnet tool update -g docfx - run: docfx docs/docfx.json - name: Deploy uses: peaceiris/actions-gh-pages@v3 with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: docs/_site
dotnet tool update -g docfx
docfx docs/docfx.json --serve
- run: dotnet tool update -g docfx - run: docfx docs/docfx.json - name: Deploy uses: peaceiris/actions-gh-pages@v3 with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: docs/_site
npm init --yes
npm install html-webpack-plugin webpack webpack-cli webpack-dev-server --save
{ "name": "remote1", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "start": "webpack serve" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "html-webpack-plugin": "^5.3.2", "webpack": "^5.57.0", "webpack-cli": "^4.8.0", "webpack-dev-server": "^4.3.1" } }
<!DOCTYPE html> <html> <head> <title> Remote1(Localhost:7001) </title> </head> <body> <div id="dev-remote1"></div> </body> </html>
const HtmlWebpackPlugin = require('html-webpack-plugin'); const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin'); module.exports = { mode: 'development', devServer: { port: 7001, }, plugins: [ new ModuleFederationPlugin({ name: 'remote1', filename: 'remoteEntry.js', exposes: { './RemoteApp1': './src/startup', }, }), new HtmlWebpackPlugin({ template: './public/index.html', }), ], };
import('./startup');
const mount = (el) => { el.innerHTML = '<div>Remote 1 Content</div>'; }; if (process.env.NODE_ENV === 'development') { const el = document.querySelector('#dev-remote1'); if (el) { mount(el); } } export { mount };
{ "name": "remote2", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "start": "webpack serve" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "html-webpack-plugin": "^5.3.2", "webpack": "^5.57.0", "webpack-cli": "^4.8.0", "webpack-dev-server": "^4.3.1" } }
<!DOCTYPE html> <html> <head> <title> Remote2(Localhost:7002) </title> </head> <body> <div id="dev-remote2"></div> </body> </html>
const HtmlWebpackPlugin = require('html-webpack-plugin'); const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin'); module.exports = { mode: 'development', devServer: { port: 7002, }, plugins: [ new ModuleFederationPlugin({ name: 'remote2', filename: 'remoteEntry.js', exposes: { './RemoteApp2': './src/startup', }, }), new HtmlWebpackPlugin({ template: './public/index.html', }), ], };
import('./startup');
const mount = (el) => { el.innerHTML = '<div>Remote 2 Content</div>'; }; if (process.env.NODE_ENV === 'development') { const el = document.querySelector('#dev-remote2'); if (el) { mount(el); } } export { mount };
{ "name": "container", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "start": "webpack serve" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "html-webpack-plugin": "^5.3.2", "webpack": "^5.57.0", "webpack-cli": "^4.8.0", "webpack-dev-server": "^4.3.1" } }
<!DOCTYPE html> <html> <head> <title>Host App (Localhost:7000)</title> </head> <body> <div id="remote1-app"></div> <div id="remote2-app"></div> </body> </html>
const HtmlWebpackPlugin = require('html-webpack-plugin'); const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin'); module.exports = { mode: 'development', devServer: { port: 7000, }, plugins: [ new ModuleFederationPlugin({ name: 'container', remotes: { remote1: 'remote1@http://localhost:7001/remoteEntry.js', remote2: 'remote2@http://localhost:7002/remoteEntry.js', }, }), new HtmlWebpackPlugin({ template: './public/index.html', }), ], };
import('./startup');
import { mount as remote1Mount } from 'remote1/RemoteApp1'; import { mount as remote2Mount } from 'remote2/RemoteApp2'; remote1Mount(document.querySelector('#remote1-app')); remote2Mount(document.querySelector('#remote2-app'));
new ModuleFederationPlugin({ name: 'remote1', filename: 'remoteEntry.js', exposes: { './RemoteApp1': './src/startup', }, shared:['react', 'react-dom'] }),
<uses-permission android:name="android.permission.RECORD_AUDIO" />
سیستمعامل مجاز به اجرای برنامهای بدون امضاء نخواهد بود.
همانطور که در تصویر فوق مشاهده میکنید، سورس کد توسط کامپایلر دات نت به exe و یا dll کامپایل میشود. کامپایلر JIT تنها متدهایی را که در زمان اجرا(runtime) فراخوانی میشوند را کامپایل میکند. در دات نت فریم ورک سه نوع JIT Compilation داریم:
Normal JIT Compilation
در این نوع کامپایل، متدها در زمان فراخوانی در زمان اجرا کامپایل میشوند. بعد از اجرا، متد داخل حافظه ذخیره میشود. به متدهای ذخیره شده در حافظه jitted گفته میشود. دیگر نیازی به کامپایل متد jit شده نیست. در فراخوانی بعدی، متد مستقیماً از حافظه کش در دسترس خواهد بود.
Econo JIT Compilation
این نوع کامپایل شبیه به حالت Normal JIT است با این تفاوت که متدها بلافاصله بعد از اجرا از حافظه حذف میشوند.
Pre-JIT Compilation
یکی دیگر از حالتهای کامپایل برنامههای دات نتی Pre-JIT Compilation می باشد. در این حالت به جای متدهای مورد استفاده، کل اسمبلی کامپایل میشود. در دات نت میتوان اینکار را توسط Ngen.exe یا (Native Image Generator) انجام داد. تمام دستورالعملهای CIL قبل از اجرا به کد محلی(Native Code) کامپایل میشوند. در این حالت runtime میتواند از native images به جای کامپایلر JIT استفاده کند. این نوع کامپایل عملیات تولید کد را در زمان اجرای برنامه به زمان Installation منتقل میکند، در اینصورت برنامه نیاز به یک Installer برای اینکار دارد.
Multicore JIT
در دات نت فریم ورک 4.5 یک راه حل جایگزین دیگر برای بهینه سازی و بهبود سرعت اجرای برنامههای دات نت وجود دارد. همانطور که عنوان شد Ngen.exe برای در دسترس بودن نیاز به Installer برای برنامه دارد. توسط Multicore JIT متدها بر روی دو هسته به صورت موازی کامپایل میشوند، در اینصورت میتوانید تا 50 درصد از JIT Time صرفه جویی کنید.
Multicore JIT همچنین میتواند باعث بهبود سرعت در برنامههای WPF شود. در نمودار زیر میتوانید حالتهای استفاده و عدم استفاده از Multicore JIT را در سه برنامه WPF نوشته شده مشاهده کنید.
Multicore JIT در عمل
Multicore JIT از دو مد عملیاتی استفاده میکند: مد ثبت(Recording mode)، مد بازپخش(Playback mode)
در حالت ثبت کامپایلر JIT هر متدی که نیاز به کامپایل داشته باشد را رکورد میکند. بعد از اینکه CLR تعیین کند که اجرای برنامه به اتمام رسیده است، تمام متدهایی که اجرا شده اند را به صورت یک پروفایل بر روی دیسک ذخیره میکند.
هنگامیکه Multicore JIT فعال میشود، با اولین اجرای برنامه، حالت ثبت مورد استفاده قرار میگیرد. در اجراهای بعدی، از حالت بازپخش استفاده میشود. حالت بازپخش پروفایل را از طریق دیسک بارگیری کرده، و قبل از اینکه این اطلاعات توسط ترد اصلی مورد استفاده قرار گیرد، از آنها برای تفسیر (کامپایل) متدها در پیشزمینه استفاده میکند.
در نتیجه، ترد اصلی به کامپایل دیگری نیاز ندارد، در این حالت سرعت اجرای برنامه بیشتر میشود. حالتهای ثبت و بازپخش تنها برای کامپیوترهایی با چندین هسته فعال میباشند.
استفاده از Multicore JIT
در برنامههای 4.5 ASP.NET و 5 Silverlight به صورت پیش فرض این ویژگی فعال میباشد. ازآنجائیکه این برنامهها hosted application هستند؛ در نتیجه فضای مناسبی برای ذخیره سازی پروفایل در این نوع برنامهها موجود میباشد. اما برای برنامههای Desktop این ویژگی باید فعال شود. برای اینکار کافی است دو خط زیر را به نقطه شروع برنامه تان اضافه کنید:
public App() { ProfileOptimization.SetProfileRoot(@"C:\MyAppFolder"); ProfileOptimization.StartProfile("Startup.Profile"); }
توسط متد SetProfileRoot میتوانیم مسیر ذخیره سازی پروفایل JIT را مشخص کنیم. در خط بعدی نیز توسط متد StartProfile نام پروفایل را برای فعال سازی Multicore JIT تعیین میکنیم. در این حالت در اولین اجرای برنامه پروفایلی وجود ندارد، Multicore JIT در حالت ثبت عمل میکند و پروفایل را در مسیر تعیین شده ایجاد میکند. در دومین بار اجرای برنامه CRL پروفایل را از اجرای قبلی برنامه بارگذاری میکند؛ در این حالت Multicore JIT به صورت بازپخش عمل میکند.
همانطور که عنوان شد در برنامههای ASP.NET 4.5 و Silverlight 5 قابلیت Multicore JIT به صورت پیش فرض فعال میباشد. برای غیر فعال سازی آن میتوانید با تغییر فلگ profileGuidedOptimizations به None اینکار را انجام دهید:
<?xml version="1.0" encoding="utf-8" ?> <configuration> <!-- ... --> <system.web> <compilation profileGuidedOptimizations="None" /> <!-- ... --> </system.web> </configuration>
routes.MapRoute( "SiteMap_route", // Route name "sitemap.xml", // URL with parameters new { controller = "Sitemap", action = "index", name = UrlParameter.Optional, area = "" } // Parameter defaults );
public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute( name: "Default", url: "{controller}/{action}/{id}", defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } ); routes.MapRoute( "SiteMap_route", // Route name "sitemap.xml", // URL with parameters new { controller = "Sitemap", action = "index", name = UrlParameter.Optional, area = "" } // Parameter defaults ); }
RouteDebug.RouteDebugger.RewriteRoutesForTesting(RouteTable.Routes);
public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute( "SiteMap_route", // Route name "sitemap.xml", // URL with parameters new { controller = "Sitemap", action = "index", name = UrlParameter.Optional, area = "" } // Parameter defaults ); routes.MapRoute( name: "Default", url: "{controller}/{action}/{id}", defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } ); }
public class Question { public string FileId { set; get; } }
using (var store = new DocumentStore { Url = "http://localhost:8080" }.Initialize()) { using (var session = store.OpenSession()) { store.DatabaseCommands.PutAttachment(key: "file/1", etag: null, data: System.IO.File.OpenRead(@"D:\Prog\packages.config"), metadata: new RavenJObject { { "Description", "توضیحات فایل" } }); var question = new Question { By = "users/Vahid", Title = "Raven Intro", Content = "Test....", FileId = "file/1" }; session.Store(question); session.SaveChanges(); } }
Request # 2: PUT - 200 ms - <system> - 201 - /static/file/1
using (var store = new DocumentStore { Url = "http://localhost:8080" }.Initialize()) { using (var session = store.OpenSession()) { var question = session.Load<Question>("questions/97"); var file1 = store.DatabaseCommands.GetAttachment(question.FileId); Console.WriteLine(file1.Size); } }
using (var store = new DocumentStore { Url = "http://localhost:8080" }.Initialize()) { using (var session = store.OpenSession()) { store.DatabaseCommands.Patch(key: "questions/97", patches: new[] { new PatchRequest { Type = PatchCommandType.Add, Name = "Answers", Value = RavenJObject.FromObject(new Answer{ By= "users/Vahid", Content="data..."}) } }); } }
PM> Install-Package RavenDB.Bundles.CascadeDelete -Pre
var comment = new Comment { PostId = post.Id }; session.Store(comment); session.Advanced.GetMetadataFor(post)["Raven-Cascade-Delete-Documents"] = RavenJToken.FromObject(new[] { comment.Id }); session.Advanced.GetMetadataFor(post)["Raven-Cascade-Delete-Attachments"] = RavenJToken.FromObject(new[] { "picture/1" }); session.SaveChanges();
PM> Install-Package RavenDB.Bundles.Versioning
session.Store(new VersioningConfiguration { Exclude = false, Id = "Raven/Versioning/DefaultConfiguration", MaxRevisions = 5 });
var lastThreeVersions = session.Advanced.GetRevisionsFor<BlogPost>(post.Id, 0, 3);
export interface IUser { id: number; name: string; email: string; password: string; }
<form> <div class="form-group"> <label form="name">Username</label> <input id="name" type="text" class="form-control" /> </div> <div class="form-group"> <label form="email">Email</label> <input id="email" type="text" class="form-control" /> </div> <div class="form-group"> <label form="password">Password</label> <input id="password" type="password" class="form-control" /> </div> <button class="btn btn-primary" type="submit">Submit</button> </form>
import { Component } from '@angular/core'; import { Control, ControlGroup, Validators } from '@angular/common'; @Component({ selector: 'signup-form', templateUrl: 'app/users/signup-form.component.html' }) export class SignupFormComponent { form = new ControlGroup({ name: new Control('', Validators.required), email: new Control('', Validators.required), password: new Control('', Validators.required) }); onSubmit(): void { console.log(this.form.value); } }
<form [ngFormModel]="form" (ngSubmit)="onSubmit()"> <div class="form-group"> <label form="name">Username</label> <input id="name" type="text" class="form-control" ngControl="name"/> <label class="text-danger" *ngIf="!form.controls['name'].valid"> Username is required. </label> </div> <div class="form-group"> <label form="email">Email</label> <input id="email" type="text" class="form-control" ngControl="email" #email="ngForm"/> <label class="text-danger" *ngIf="email.touched && !email.valid"> Email is required. </label> </div> <div class="form-group"> <label form="password">Password</label> <input id="password" type="password" class="form-control" ngControl="password" #password="ngForm"/> <label class="text-danger" *ngIf="password.touched && !password.valid"> Password is required. </label> </div> <button class="btn btn-primary" type="submit">Submit</button> </form>
form = new ControlGroup({ name: new Control('', Validators.required), email: new Control('', Validators.required), password: new Control('', Validators.required) });
import { Control, ControlGroup, Validators } from '@angular/common';
<form [ngFormModel]="form" (ngSubmit)="onSubmit()">
<label class="text-danger" *ngIf="!form.controls['name'].valid">
onSubmit(): void { console.log(this.form.value); }
//same as before... import { SignupFormComponent } from './users/signup-form.component'; @Component({ //same as before… template: ` //same as before… <li><a [routerLink]="['AddUser']">Add User</a></li> //same as before… `, //same as before… }) @RouteConfig([ //same as before… { path: '/adduser', name: 'AddUser', component: SignupFormComponent } ]) //same as before...
import { Control, ControlGroup, Validators, FormBuilder } from '@angular/common'; form: ControlGroup; constructor(formBuilder: FormBuilder) { this.form = formBuilder.group({ name: ['', Validators.required], email: ['', Validators.required], password: ['', Validators.required] }); }
import { Control } from '@angular/common'; export class UsernameValidators { static cannotContainSpace(control: Control) { if (control.value.indexOf(' ') >= 0) { return { cannotContainSpace: true }; } return null; } }
{ required:true }
{ minlength : { requiredLength : 3, actualLength : 1 } }
import { UsernameValidators } from './usernameValidators';
name: ['', Validators.compose([Validators.required, UsernameValidators.cannotContainSpace])],
<div class="form-group"> <label form="name">Username</label> <input id="name" type="text" class="form-control" ngControl="name" #name="ngForm" /> <div *ngIf="name.touched && name.errors"> <label class="text-danger" *ngIf="name.errors.required"> Username is required. </label> <label class="text-danger" *ngIf="name.errors.cannotContainSpace"> Username can't contain space. </label> </div> </div>
import {Control} from '@angular/common'; export class EmailValidators { static email(control: Control) { var regEx = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; var valid = regEx.test(control.value); return valid ? null : { email: true }; } }
control(value: Object, validator?: ValidatorFn, asyncValidator?: AsyncValidatorFn) : Control
nameShouldBeUnique(control: Control) { let name: string = control.value; return new Promise((resolve) => { this._userService.isUserNameUnique(<IUser>{ "name": name }).subscribe( (result: IResult) => { resolve( result.result ? null : { 'nameShouldBeUnique': true } ); }, error => { resolve(null); } ); }); }
this.form = _formBuilder.group({ name: ['', Validators.compose([ Validators.required, UsernameValidators.cannotContainSpace ]), (control: Control) => this.nameShouldBeUnique(control)],
import { Injectable } from '@angular/core'; import { Http, Response } from '@angular/http'; import { Observable } from 'rxjs/Observable'; import { Headers, RequestOptions } from '@angular/http'; import { IUser } from './user'; import { IResult } from './result'; @Injectable() export class UserService { private _checkUserUrl = '/home/checkUser'; constructor(private _http: Http) { } private handleError(error: Response) { console.error(error); return Observable.throw(error.json().error || 'Server error'); } isUserNameUnique(user: IUser): Observable<IResult> { let headers = new Headers({ 'Content-Type': 'application/json' }); // for ASP.NET MVC let options = new RequestOptions({ headers: headers }); return this._http.post(this._checkUserUrl, JSON.stringify(user), options) .map((response: Response) => <IResult>response.json()) .do(data => console.log("User: " + JSON.stringify(data))) .catch(this.handleError); } }
export interface IResult { result: boolean; }
namespace MVC5Angular2.Controllers { public class HomeController : Controller { [HttpPost] public ActionResult CheckUser(User user) { var isUnique = new { result = true }; if (user.Name?.Equals("Vahid", StringComparison.OrdinalIgnoreCase) ?? false) { isUnique = new { result = false }; } return new ContentResult { Content = JsonConvert.SerializeObject(isUnique, new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() }), ContentType = "application/json", ContentEncoding = Encoding.UTF8 }; } } }
@Component({ selector: 'signup-form', templateUrl: 'app/users/signup-form.component.html', providers: [ UserService ] }) export class SignupFormComponent { constructor(private _formBuilder: FormBuilder, private _userService: UserService) {
<div *ngIf="name.touched && name.errors"> <label class="text-danger" *ngIf="name.errors.required"> Username is required. </label> <label class="text-danger" *ngIf="name.errors.cannotContainSpace"> Username can't contain space. </label> <label class="text-danger" *ngIf="name.errors.nameShouldBeUnique"> This username is already taken. </label> </div>
<input id="name" type="text" class="form-control" ngControl="name" #name="ngForm" /> <div *ngIf="name.control.pending"> Checking server, Please wait ... </div>
onSubmit(): void { console.log(this.form.value); this.form.find('name').setErrors({ invalidData : true }); }
<div *ngIf="name.touched && name.errors"> <label class="text-danger" *ngIf="name.errors.invalidData"> Check the inputs.... </label> </div>
{ name="VahidN", email="email@site.com", password="123"}
import { Injectable } from '@angular/core'; import { Http, Response } from '@angular/http'; import { Observable } from 'rxjs/Observable'; import { Headers, RequestOptions } from '@angular/http'; import { IUser } from './user'; import { IResult } from './result'; @Injectable() export class UserService { private _addUserUrl = '/home/addUser'; constructor(private _http: Http) { } private handleError(error: Response) { console.error(error); return Observable.throw(error.json().error || 'Server error'); } addUser(user: IUser): Observable<IUser> { let headers = new Headers({ 'Content-Type': 'application/json' }); // for ASP.NET MVC let options = new RequestOptions({ headers: headers }); return this._http.post(this._addUserUrl, JSON.stringify(user), options) .map((response: Response) => <IUser>response.json()) .do(data => console.log("User: " + JSON.stringify(data))) .catch(this.handleError); } }
[HttpPost] public ActionResult AddUser(User user) { user.Id = 1; //todo: save user and get id from db return new ContentResult { Content = JsonConvert.SerializeObject(user, new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() }), ContentType = "application/json", ContentEncoding = Encoding.UTF8 }; }
onSubmit(): void { console.log(this.form.value); /*this.form.find('name').setErrors({ invalidData : true });*/ this._userService.addUser(<IUser>this.form.value) .subscribe((user: IUser) => { console.log(`ID: ${user.id}`); }); }