اگر میخواهید تمام مراحل ذکر شده را فقط با دو دستور ساده به پایان برسانید:
الف) ابتدا وابستگیهای nodejs را نصب کنید.
ب) سپس angular-cli را نصب کنید (اجرای دستور عمومی ذیل در خط فرمان):
npm install -g angular-cli
ng new AngularCLIDemoApp
npm install -g angular-cli
ng new AngularCLIDemoApp
یک نکتهی تکمیلی: چگونه آنالایزرهای بسیار کند را پیدا کنیم؟!
پس از غنیسازی پروژه با تعدادی Roslyn Analyzer، با اولین موردی که مواجه خواهیم شد، بالا رفتن زمان کامپایل است؛ گاهی از اوقات تا حدی که کار کامپایل، چند دقیقه طول میکشد. در این حالت شاید این سؤال مطرح شود که کدامیک از این آنالایزرهای اضافه شده، بیشترین زمان را به خودش اختصاص داده؟
برای پاسخ به این سؤال، فقط کافی است پروژه را با سوئیچ bl/، کامپایل کنیم:
dotnet build /bl
خروجی آن که یک فایل باینری است به نام msbuild.binlog، توسط برنامهی «MSBuild Binary and Structured Log Viewer» قابل مشاهدهاست:
پس از باز کردن این فایل در برنامهی یاد شده، میتوان ذیل قسمت Analyzer summary (مانند تصویر فوق)، زمان صرف شدهی توسط هر آنالایزر را مشاهده کرد و در صورت نیاز، موارد زمانبر را از پروژه حذف کرد.
یا حتی میتوانید اجرای تمام آنالایزرها را برای حالت دیباگ، غیرفعال کنید:
dotnet run -p:RunAnalyzers=false
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.Add(
new Route("{controller}.aspx/{action}/{id}", new MvcRouteHandler())
{
Defaults = new RouteValueDictionary(new
{
controller = "Home",
action = "Index",
id = UrlParameter.Optional
})
});
Right-click on a web site -> Properties -> Home Directory tab -> click on the Configuration button -> Mappings tab
C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\aspnet_regiis.exe /i
namespace jQueryMvcSample02.Models { public class BlogPost { public int Id { set; get; } public string Title { set; get; } public string Body { set; get; } } }
using System.Collections.Generic; using System.Linq; using jQueryMvcSample02.Models; namespace jQueryMvcSample02.DataSource { public static class BlogPostDataSource { private static IList<BlogPost> _cachedItems; /// <summary> /// با توجه به استاتیک بودن سازنده کلاس، تهیه کش، پیش از سایر فراخوانیها صورت خواهد گرفت /// باید دقت داشت که این فقط یک مثال است و چنین کشی به معنای /// تهیه یک لیست برای تمام کاربران سایت است /// </summary> static BlogPostDataSource() { _cachedItems = createBlogPostsInMemoryDataSource(); } /// <summary> /// هدف صرفا تهیه یک منبع داده آزمایشی ساده تشکیل شده در حافظه است /// </summary> private static IList<BlogPost> createBlogPostsInMemoryDataSource() { var results = new List<BlogPost>(); for (int i = 1; i < 30; i++) { results.Add(new BlogPost { Id = i, Title = "عنوان " + i, Body = "متن ... متن ... متن " + i }); } return results; } /// <summary> /// پارامترهای شماره صفحه و تعداد رکورد به ازای یک صفحه برای صفحه بندی نیاز هستند /// شماره صفحه از یک شروع میشود /// </summary> public static IList<BlogPost> GetLatestBlogPosts(int pageNumber, int recordsPerPage = 4) { var skipRecords = pageNumber * recordsPerPage; return _cachedItems .OrderByDescending(x => x.Id) .Skip(skipRecords) .Take(recordsPerPage) .ToList(); } } }
using System.Linq; using System.Web.Mvc; using System.Web.UI; using jQueryMvcSample02.DataSource; using jQueryMvcSample02.Security; namespace jQueryMvcSample02.Controllers { public class HomeController : Controller { [HttpGet] public ActionResult Index() { //آغاز کار با صفحه صفر است var list = BlogPostDataSource.GetLatestBlogPosts(pageNumber: 0); return View(list); //نمایش ابتدایی صفحه } [HttpPost] [AjaxOnly] [OutputCache(Location = OutputCacheLocation.None, NoStore = true)] public virtual ActionResult PagedIndex(int? page) { var pageNumber = page ?? 0; var list = BlogPostDataSource.GetLatestBlogPosts(pageNumber); if (list == null || !list.Any()) return Content("no-more-info"); //این شرط ما است برای نمایش عدم یافتن رکوردها return PartialView("_ItemsList", list); } [HttpGet] public ActionResult Post(int? id) { if (id == null) return Redirect("/"); //todo: show the content here return Content("Post " + id.Value); } } }
@model IList<jQueryMvcSample02.Models.BlogPost> <ul> @foreach (var item in Model) { <li> <h5> @Html.ActionLink(linkText: item.Title, actionName: "Post", controllerName: "Home", routeValues: new { id = item.Id }, htmlAttributes: null) </h5> @item.Body </li> } </ul>
@model IList<jQueryMvcSample02.Models.BlogPost> @{ ViewBag.Title = "Index"; var loadInfoUrl = Url.Action(actionName: "PagedIndex", controllerName: "Home"); } <h2> اسکرول نامحدود</h2> @{ Html.RenderPartial("_ItemsList", Model); } <div id="MoreInfoDiv"> </div> <div align="center" style="margin-bottom: 9px;"> <span id="moreInfoButton" style="width: 90%;" class="btn btn-info">بیشتر</span> </div> <div id="ProgressDiv" align="center" style="display: none"> <br /> <img src="@Url.Content("~/Content/images/loadingAnimation.gif")" alt="loading..." /> </div> @section JavaScript { <script type="text/javascript"> $(document).ready(function () { $("#moreInfoButton").InfiniteScroll({ moreInfoDiv: '#MoreInfoDiv', progressDiv: '#ProgressDiv', loadInfoUrl: '@loadInfoUrl', loginUrl: '/login', errorHandler: function () { alert('خطایی رخ داده است'); }, completeHandler: function () { // اگر قرار است روی اطلاعات نمایش داده شده پردازش ثانوی صورت گیرد }, noMoreInfoHandler: function () { alert('اطلاعات بیشتری یافت نشد'); } }); }); </script> }
// <![CDATA[ (function ($) { $.fn.InfiniteScroll = function (options) { var defaults = { moreInfoDiv: '#MoreInfoDiv', progressDiv: '#Progress', loadInfoUrl: '/', loginUrl: '/login', errorHandler: null, completeHandler: null, noMoreInfoHandler: null }; var options = $.extend(defaults, options); var showProgress = function () { $(options.progressDiv).css("display", "block"); } var hideProgress = function () { $(options.progressDiv).css("display", "none"); } return this.each(function () { var moreInfoButton = $(this); var page = 1; $(moreInfoButton).click(function (event) { showProgress(); $.ajax({ type: "POST", url: options.loadInfoUrl, data: JSON.stringify({ page: page }), contentType: "application/json; charset=utf-8", dataType: "json", complete: function (xhr, status) { var data = xhr.responseText; if (xhr.status == 403) { window.location = options.loginUrl; } else if (status === 'error' || !data) { if (options.errorHandler) options.errorHandler(this); } else { if (data == "no-more-info") { if (options.noMoreInfoHandler) options.noMoreInfoHandler(this); } else { var $boxes = $(data); $(options.moreInfoDiv).append($boxes); } page++; } hideProgress(); if (options.completeHandler) options.completeHandler(this); } }); }); }); }; })(jQuery); // ]]>
namespace BlazorWasmSQLite.Models; public class Car { public int Id { get; set; } public string Brand { get; set; } public int Price { get; set; } }
using Microsoft.EntityFrameworkCore; using BlazorWasmSQLite.Models; namespace BlazorWasmSQLite.Data; public class ClientSideDbContext : DbContext { public DbSet<Car> Cars { get; set; } = default!; public ClientSideDbContext(DbContextOptions<ClientSideDbContext> options) : base(options) { } }
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly"> <ItemGroup> <!-- EF Core and Sqlite --> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.1" /> </ItemGroup> </Project>
using Microsoft.AspNetCore.Components.Web; using Microsoft.AspNetCore.Components.WebAssembly.Hosting; using BlazorWasmSQLite; using Microsoft.EntityFrameworkCore; using BlazorWasmSQLite.Data; var builder = WebAssemblyHostBuilder.CreateDefault(args); builder.RootComponents.Add<App>("#app"); builder.RootComponents.Add<HeadOutlet>("head::after"); builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); // Sets up EF Core with Sqlite builder.Services.AddDbContextFactory<ClientSideDbContext>(options => options .UseSqlite($"Filename=DemoData.db") .EnableSensitiveDataLogging()); await builder.Build().RunAsync();
@page "/" @using Microsoft.Data.Sqlite @using Microsoft.EntityFrameworkCore @using BlazorWasmSQLite.Data @using BlazorWasmSQLite.Models <PageTitle>Index</PageTitle> <h1>Hello, world!</h1> Welcome to your new app. <SurveyPrompt Title="How is Blazor working for you?" /> @code { [Inject] private IDbContextFactory<ClientSideDbContext> _dbContextFactory { get; set; } = default!; protected override async Task OnInitializedAsync() { await using var db = await _dbContextFactory.CreateDbContextAsync(); await db.Database.EnsureCreatedAsync(); // create seed data if (!db.Cars.Any()) { var cars = new[] { new Car { Brand = "Audi", Price = 21000 }, new Car { Brand = "Volvo", Price = 11000 }, new Car { Brand = "Range Rover", Price = 135000 }, new Car { Brand = "Ford", Price = 8995 } }; await db.Cars.AddRangeAsync(cars); await db.SaveChangesAsync(); } await base.OnInitializedAsync(); } }
crit: Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100] Unhandled exception rendering component: The type initializer for 'Microsoft.Data.Sqlite.SqliteConnection' threw an exception. System.TypeInitializationException: The type initializer for 'Microsoft.Data.Sqlite.SqliteConnection' threw an exception. ---> System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.DllNotFoundException: e_sqlite3 at SQLitePCL.SQLite3Provider_e_sqlite3.SQLitePCL.ISQLite3Provider.sqlite3_libversion_number()
$ git clone https://github.com/cloudmeter/sqlite $ cd sqlite $ emcc sqlite3.c -shared -o e_sqlite3.o
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly"> <ItemGroup> <!-- EF Core and Sqlite --> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.1" /> <NativeFileReference Include="Data\e_sqlite3.o" /> </ItemGroup> </Project>
npm ERR! Windows_NT 10.0.10586 npm ERR! argv "C:\\Program Files\\nodejs\\node.exe" "C:\\Program Files\\nodejs\\node_modules\\npm\\bin\\npm-cli.js" "install" "-g" "@angular/cli" npm ERR! node v7.10.0 npm ERR! npm v4.2.0 npm ERR! Cannot read property 'path' of null npm ERR! npm ERR! If you need help, you may report this error at: npm ERR! <https://github.com/npm/npm/issues> npm ERR! Please include the following file with any support request: npm ERR! C:\Users\payervand\AppData\Roaming\npm-cache\_logs\2017-05-13T05_43_04_935Z-debug.log
در فایل ProductController.ts کدهای زیر را کپی نمایید:
module Product { export interface Scope { message: string; } export class Controller { constructor($scope: Scope) { $scope.message = "Hello from Masoud"; } } }
ابتدا یک ماژول به نام Product ایجاد میکنیم. سپس یک اینترفیس برای پیاده سازی آبجکت Scope که جهت مقید سازی عناصر DOM به آبجکتهای کنترلر مورد استفاده قرار میگیرد، ایجاد میکنیم. در داخل این اینترفیس متغیری به نام message از نوع string داریم. قصد داریم این متغیر را به یک عنصر مقید کنیم. حال یک کلاس به نام کنترلر ایجاد میکنیم که در تابع سازنده آن تزریق وابستگی برای scope$ از نوع اینترفیس Scope تعیین شده است. در نتیجه در بدنه سازنده میتوانیم به متغیر message مقدار مورد نظر را نسبت دهیم .
کلمه کلیدی
export برای تعریف عمومی کلاس استفاده شده است .
یک View ایجاد و کدهای
زیر را در آن کپی کنید :
<script type="text/javascript" src="~/scripts/app/ProductController.js"></script> <div ng-app> <div ng-controller="Product.Controller"> <p>{{message}}</p> </div> </div>
اولین نکته در تگ script است که فراخوانی فایل TypeScript باید با پسوند js. انجام گیرد. به دلیل اینکه فایلهای TypeScript بعد از کامپایل تبدیل به فایلهای JavaScript خواهند شد؛ در نتیجه پسوند آن نیز js. است. دومین نکته در فراخوانی کنترلر مورد نظر است که از ترکیب نام ماژول و نام کلاس است. بعد از اجرای پروژه خروجی به صورت زیر خواهد بود :
public class Northwind : DataService< /* TODO: put your data source class name here */ > { // This method is called only once to initialize service-wide policies. public static void InitializeService(DataServiceConfiguration config) { // TODO: set rules to indicate which entity sets and service operations are visible, updatable, etc. // Examples: // config.SetEntitySetAccessRule("MyEntityset", EntitySetRights.AllRead); // config.SetServiceOperationAccessRule("MyServiceOperation", ServiceOperationRights.All); config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V3; } }
public class Northwind : DataService<NorthwindEntities>
config.SetEntitySetAccessRule("Orders", EntitySetRights.AllRead | EntitySetRights.WriteMerge | EntitySetRights.WriteReplace ); config.SetEntitySetAccessRule("Customers", EntitySetRights.AllRead);
config.SetEntitySetAccessRule("*", EntitySetRights.AllRead);
<service xmlns="http://www.w3.org/2007/app" xmlns:atom="http://www.w3.org/2005/Atom" xml:base="http://localhost:8358/Northwind.svc/"> <workspace> <atom:title>Default</atom:title> <collection href="Categories"> <atom:title>Categories</atom:title> </collection> <collection href="Customers"> <atom:title>Customers</atom:title> </collection> <collection href="Employees"> <atom:title>Employees</atom:title> </collection> <collection href="Order_Details"> <atom:title>Order_Details</atom:title> </collection> <collection href="Orders"> <atom:title>Orders</atom:title> </collection> <collection href="Products"> <atom:title>Products</atom:title> </collection> <collection href="Shippers"> <atom:title>Shippers</atom:title> </collection> <collection href="Suppliers"> <atom:title>Suppliers</atom:title> </collection> </workspace> </service>
<Window x:Class="MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Northwind Orders" Height="335" Width="425" Name="OrdersWindow" Loaded="Window1_Loaded"> <Grid Name="orderItemsGrid"> <ComboBox DisplayMemberPath="Order_ID" ItemsSource="{Binding}" IsSynchronizedWithCurrentItem="true" Height="23" Margin="92,12,198,0" Name="comboBoxOrder" VerticalAlignment="Top"/> <DataGrid ItemsSource="{Binding Path=Order_Details}" CanUserAddRows="False" CanUserDeleteRows="False" Name="orderItemsDataGrid" Margin="34,46,34,50" AutoGenerateColumns="False"> <DataGrid.Columns> <DataGridTextColumn Header="Product" Binding="{Binding Product_ID, Mode=OneWay}" /> <DataGridTextColumn Header="Quantity" Binding="{Binding Quantity, Mode=TwoWay}" /> <DataGridTextColumn Header="Price" Binding="{Binding UnitPrice, Mode=TwoWay}" /> <DataGridTextColumn Header="Discount" Binding="{Binding Discount, Mode=TwoWay}" /> </DataGrid.Columns> </DataGrid> <Label Height="28" Margin="34,12,0,0" Name="orderLabel" VerticalAlignment="Top" HorizontalAlignment="Left" Width="65">Order:</Label> <StackPanel Name="Buttons" Orientation="Horizontal" HorizontalAlignment="Right" Height="40" Margin="0,257,22,0"> <Button Height="23" HorizontalAlignment="Right" Margin="0,0,12,12" Name="buttonSave" VerticalAlignment="Bottom" Width="75" Click="buttonSaveChanges_Click">Save Changes </Button> <Button Height="23" Margin="0,0,12,12" Name="buttonClose" VerticalAlignment="Bottom" Width="75" Click="buttonClose_Click">Close</Button> </StackPanel> </Grid> </Window>
private NorthwindEntities context; private string customerId = "ALFKI"; private Uri svcUri = new Uri("http://localhost:8358/Northwind.svc"); private void Window1_Loaded(object sender, RoutedEventArgs e) { try { context = new NorthwindEntities(svcUri); var ordersQuery = from o in context.Orders.Expand("Order_Details") where o.Customers.Customer_ID == customerId select o; DataServiceCollection<Orders> customerOrders = new DataServiceCollection<Orders>(ordersQuery); this.orderItemsGrid.DataContext = customerOrders; } catch (Exception ex) { MessageBox.Show(ex.ToString()); } }
private void buttonSaveChanges_Click(object sender, RoutedEventArgs e) { try { context.SaveChanges(); } catch (DataServiceRequestException ex) { MessageBox.Show(ex.ToString()); } }
using System.Collections.Generic; using System.Runtime.CompilerServices; namespace ConditionalWeakTableSamples { public static class AttachedProperties { public static ConditionalWeakTable<object, Dictionary<string, object>> ObjectCache = new ConditionalWeakTable<object, Dictionary<string, object>>(); public static void SetValue<T>(this T obj, string name, object value) where T : class { var properties = ObjectCache.GetOrCreateValue(obj); if (properties.ContainsKey(name)) properties[name] = value; else properties.Add(name, value); } public static T GetValue<T>(this object obj, string name) { Dictionary<string, object> properties; if (ObjectCache.TryGetValue(obj, out properties) && properties.ContainsKey(name)) return (T)properties[name]; return default(T); } public static object GetValue(this object obj, string name) { return obj.GetValue<object>(name); } } }
public static class ISecurityPrincipalExtension { public static bool Disclaimer(this IPrincipal principal) { return principal.GetValue<bool>("Disclaimer"); } public static void SetDisclaimer(this IPrincipal principal, bool value) { principal.SetValue("Disclaimer", value); } }
using System.Runtime.CompilerServices; namespace ConditionalWeakTableSamples { public static class PropertyExtensions { private class ExtraPropertyHolder { public bool IsDirty { get; set; } } private static readonly ConditionalWeakTable<object, ExtraPropertyHolder> _isDirtyTable = new ConditionalWeakTable<object, ExtraPropertyHolder>(); public static bool IsDirty(this object @this) { return _isDirtyTable.GetOrCreateValue(@this).IsDirty; } public static void SetIsDirty(this object @this, bool isDirty) { _isDirtyTable.GetOrCreateValue(@this).IsDirty = isDirty; } } }
public void Closed(DbConnection connection, DbConnectionInterceptionContext interceptionContext) { }
using System; using System.Runtime.CompilerServices; namespace ConditionalWeakTableSamples { public static class UniqueIdExtensions { static readonly ConditionalWeakTable<object, string> _idTable = new ConditionalWeakTable<object, string>(); public static string GetUniqueId(this object obj) { return _idTable.GetValue(obj, o => Guid.NewGuid().ToString()); } public static string GetUniqueId(this object obj, string key) { return _idTable.GetValue(obj, o => key); } } }