در این نوشتار که به صورت آموزش تصویری ارائه میشود؛ یک سرویس WCF در Visual Studio 2013 ایجاد میکنم، سپس روش استفاده از آنرا در یک برنامه ویندوزی آموزش خواهم داد. در اینجا در نظرگرفته شده است که شما افزونهی Resharper را روی ویژوال استودیوی خود نصب دارید. پس در صورتیکه هنوز به سراغ آن نرفته اید درنگ نکنید و واپسین نگارش آن را دانلود کنید.
در این پروژهی ساده در نظر میگیریم که دو جدول یکی برای اخبار، شامل عنوان، متن خبر و تاریخ ثبت و دسته بندی و دیگری برای نگهداری دستهها در پایگاه داده داریم و میخواهیم سرویسهای مناسب با این دو جدول را بسازیم. با کد زیر، پایگاه دادهی dbTest و جدولهای tblNews و tblCategory در SQL Server 2012 ساخته میشود:
USE [master] GO /****** Object: Database [dbMyNews] Script Date: 2014/01/14 09:46:04 ب.ظ ******/ CREATE DATABASE [dbMyNews] CONTAINMENT = NONE ON PRIMARY ( NAME = N'dbMyNews', FILENAME = N'D:\dbMyNews.mdf' , SIZE = 5120KB , MAXSIZE = UNLIMITED, FILEGROWTH = 1024KB ) LOG ON ( NAME = N'dbMyNews_log', FILENAME = N'D:\dbMyNews_log.ldf' , SIZE = 1024KB , MAXSIZE = 2048GB , FILEGROWTH = 10%) GO USE [dbMyNews] GO /****** Object: Table [dbo].[tblCategory] Script Date: 2014/01/14 09:46:04 ب.ظ ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO CREATE TABLE [dbo].[tblCategory]( [tblCategoryId] [int] IDENTITY(1,1) NOT NULL, [CatName] [nvarchar](50) NOT NULL, [IsDeleted] [bit] NOT NULL, CONSTRAINT [PK_tblCategory] PRIMARY KEY CLUSTERED ( [tblCategoryId] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] GO /****** Object: Table [dbo].[tblNews] Script Date: 2014/01/14 09:46:04 ب.ظ ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO CREATE TABLE [dbo].[tblNews]( [tblNewsId] [int] IDENTITY(1,1) NOT NULL, [tblCategoryId] [int] NOT NULL, [Title] [nvarchar](50) NOT NULL, [Description] [nvarchar](max) NOT NULL, [RegDate] [datetime] NOT NULL, [IsDeleted] [bit] NULL, CONSTRAINT [PK_tblNews] PRIMARY KEY CLUSTERED ( [tblNewsId] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] GO ALTER TABLE [dbo].[tblNews] WITH CHECK ADD CONSTRAINT [FK_tblNews_tblCategory] FOREIGN KEY([tblCategoryId]) REFERENCES [dbo].[tblCategory] ([tblCategoryId]) GO ALTER TABLE [dbo].[tblNews] CHECK CONSTRAINT [FK_tblNews_tblCategory] GO USE [master] GO ALTER DATABASE [dbMyNews] SET READ_WRITE GO
اکنون Visual Studio 2013 را بازکنید سپس روی گزینه New Project کلیک کنید و برابر با نگارهی زیر عمل کنید:
پروژه MyNewsWCFLibrary در راه حل MyNews ساخته میشود. این پروژه به صورت پیشگزیده دارای یک کلاس به نام Service و یک interface به نام IService است. هر دو را حذف کنید و سپس روی نام پروژه راستکلیک کرده، از منوی بازشده گزینهی Add -> New Item را انتخاب کنید. سپس برابر با نگارهی زیر عمل کنید:
در لایهی Service Interface کلیهی روالهای مورد نیاز برای ارتباط با پایگاه داده را میسازیم. پیش از آن باید یک Model برای ارتباط با پایگاه داده ساخته باشیم. برای این کار از پنجره Add New Item و از زیرمجموعه Data، گزینه ADO.NET Entity Data Model را انتخاب کنید و بهسان زیر پیش روید:
در گام پسین روی دکمه New Connection کلیک کنید و رشتهی اتصال به پایگاه دادهی dbMyNews را بسازید. سپس همانند تنظیمات نگارهی زیر ادامه دهید:
در گام پسین گزینهی Entity Framework 6.0 را برگزینید و روی دکمهی Next کلیک کنید.
در پنجره نشان داده شده، جدولهای مورد نیاز را همانند نگارهی زیر انتخاب کرده و روی دکمه Finish کلیک کنید:
در پایان مدل ما همانند نگارهی زیر خواهد بود.
در بخش پسین دربارهی شیوهی دستکاری کلاسهای Entity خواهم نوشت.
SignalR
عموما در محیط کاری اگر شبکه ویندوزی و mail server مورد استفاده هم ms exchange باشد، به طور قطع از outlook برای انجام امور روزمره ارسال و دریافت ایمیل استفاده میشود.
طبق معمول هم مشکل ما تاریخ فارسی است! یکی از شرکتهای ایرانی که در اینباره محصولی را ارائه داده با hook کردن تاریخ ویندوز، هر جایی که تاریخی قرار است نمایش داده شود، آنرا فارسی میکند. این محصول دو ایراد دارد: الف) رایگان نیست! ب) این hook بر روی عملکرد سایر برنامهها تاثیرگذار است. برای مثال برنامههای دات نت تاریخ قمری را نمایش خواهند داد، بر روی عملکرد و کارآیی کلی سیستم تاثیر منفی دارد و مشکلاتی از این دست.
افزونه زیر بدون دستکاری تاریخ ویندوز، دو کار را در MS outlook 2007 انجام خواهد داد:
الف) اضافه کردن ستون "تاریخ دریافت" شمسی
ب) در متن دریافتی، تمام تاریخهای sent موجود را یافته و شمسی میکند
دریافت افزونه:
لطفا اینجا کلیک کنید.
تذکر مهم:
نصب دو بسته به روز رسانی سیستم و دات نت فریم ورک را پیش از نصب این افزونه فراموش نکنید.
/Post/14/افزونه-فارسی-به-پارسی-برای-word-2007
در کل برای من کار راه انداز بوده :)
در طی روزهای آتی، سورس کامل و نحوه برنامه نویسی آنرا بررسی خواهیم کرد.
الف) در متدهای لایه جاری خود واژههای کلیدی new و همچنین کلیه فراخوانیهای استاتیک را بیابید.
ب) وهله سازی اینها را به یک سطح بالاتر (نقطه آغازین برنامه) منتقل کنید. اینکار باید بر اساس اتکای به Abstraction و برای مثال استفاده از اینترفیسها صورت گیرد.
ج) اینکار را آنقدر تکرار کنید تا دیگر در کدهای لایه جاری خود واژه کلیدی new یا فراخوانی متدهای استاتیک مشاهده نشود.
د) در آخر وهله سازی object graphهای مورد نیاز را به یک IoC Container محول کنید.
یک مثال: ابتدا بررسی یک قطعه کد متداول
using System.Net; using System.Text; using System.Text.RegularExpressions; using System.Web.Mvc; namespace DI06.Controllers { public class HomeController : Controller { public ActionResult Index() { string result = string.Empty; using (var client = new WebClient { Encoding = Encoding.UTF8 }) { result = client.DownloadString("https://www.dntips.ir/"); } var match = new Regex(@"(?s)<title>(.+?)</title>", RegexOptions.IgnoreCase).Match(result); var title = match.Groups[1].Value.Trim(); ViewBag.PageTitle = title; return View(); } } }
مشکلات کد فوق:
الف) قرار گرفتن منطق تجاری پیاده سازی کدها مستقیما داخل کدهای یک اکشن متد؛ این مساله در دراز مدت به تکرار شدید کدها منجر خواهد شد که نهایتا قابلیت نگهداری آنرا کاهش میدهند.
ب) در این کد حداقل دو بار واژه کلیدی new ذکر شده است. مورد اول یا new WebClient، از همه مهمتر است؛ از این جهت که نوشتن آزمون واحد را برای این کنترلر بسیار مشکل میکند. آزمونهای واحد باید سریع و بدون نیاز به منابع خارجی، قابل اجرا باشند. تعویض آن هم مطابق کدهای تدارک دیده شده کار سادهای نیست.
بهبود کیفیت قطعه کد متداول فوق با استفاده از الگوی معکوس سازی وابستگیها
در اصل معکوس سازی وابستگیها عنوان کردیم لایه بالایی سیستم نباید مستقیما به لایههای زیرین در حال استفاده از آن، وابسته باشد. این وابستگی باید معکوس شده و همچنین بر اساس Abstraction یا برای مثال استفاده از اینترفیسها صورت گیرد.
به همین منظور یک پروژه دیگر را از نوع Class library، مثلا به نام DI06.Services به Solution جاری اضافه میکنیم.
namespace DI06.Services { public interface IWebClientServices { string FetchUrl(string url); string GetWebPageTitle(string url); } } using System.Net; using System.Text; using System.Text.RegularExpressions; namespace DI06.Services { public class WebClientServices : IWebClientServices { public string FetchUrl(string url) { using (var client = new WebClient { Encoding = Encoding.UTF8 }) { return client.DownloadString(url); } } public string GetWebPageTitle(string url) { var html = FetchUrl(url); var match = new Regex(@"(?s)<title>(.+?)</title>", RegexOptions.IgnoreCase).Match(html); return match.Groups[1].Value.Trim(); } } }
هنوز کار معکوس سازی وابستگیها رخ نداده است. صرفا اندکی تمیزکاری و انتقال پیاده سازی منطق تجاری به یک سری کلاسهایی با قابلیت استفاده مجدد صورت گرفته است. به این ترتیب اگر باگی در این کدها وجود داشته باشد و همچنین از آن در چندین نقطه برنامه استفاده شده باشد، اصلاح این کلاس مرکزی، به یکباره تمامی قسمتهای مختلف برنامه را تحت تاثیر مثبت قرار داده و از تکرار کدها و فراموشی احتمالی بهبود قسمتهای مشابه جلوگیری میکند.
کار معکوس سازی وابستگیها در یک لایه بالاتر صورت خواهد گرفت:
using System.Web.Mvc; using DI06.Services; namespace DI06.Controllers { public class HomeController : Controller { readonly IWebClientServices _webClientServices; public HomeController(IWebClientServices webClientServices) { _webClientServices = webClientServices; } public ActionResult Index() { ViewBag.PageTitle = _webClientServices.GetWebPageTitle("https://www.dntips.ir/"); return View(); } } }
در مورد نحوه تنظیمات اولیه یک IoC Container و یا پیشنیازهای ASP.NET MVC جهت آماده شدن برای تزریق خودکار وابستگیها در سازنده کنترلرها، پیشتر مطالبی را در این سری مطالعه کردهاید؛ در اینجا نیز اصول مورد استفاده یکی است و تفاوتی نمیکند.
مهارتهای تزریق وابستگیها در برنامههای NET Core. - قسمت سوم - رهاسازی منابع سرویسهای IDisposable
رفتار IoC Container توکار ASP.NET Core با سرویسهای IDisposable
ASP.NET Core به همراه یک IoC Container توکار ارائه میشود و اگر سرویسی با طول عمرTransient و یا Scoped به آن معرفی شود و همچنین این سرویس اینترفیس IDisposable را نیز پیاده سازی کند، کار dispose خودکار آن در پایان درخواست جاری صورت میگیرد و نیازی به تنظیمات اضافهتری ندارد. در اینجا سرویسهایی با طول عمر Singleton نیز در پایان کار برنامه، زمانیکه خود ServiceProvider به پایان کارش میرسد، dispose خواهند شد.
البته این مورد یک شرط را نیز به همراه دارد: کار وهله سازی سرویسهای درخواستی باید توسط خود این IoC Container مدیریت شود تا در پایان کار بداند چگونه آنها را Dispose کند.
یک مثال: بررسی Dispose شدن خودکار یک سرویس IDisposable
namespace CoreIocServices { public interface IMyDisposableService { void Run(); } public class MyDisposableService : IMyDisposableService, IDisposable { private readonly ILogger<MyDisposableService> _logger; public MyDisposableService(ILogger<MyDisposableService> logger) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _logger.LogInformation("+ {0} was created", this.GetType().Name); } public void Run() { _logger.LogInformation("Running MyDisposableService!"); } public void Dispose() { _logger.LogInformation("- {0} was disposed!", this.GetType().Name); } } }
اگر این سرویس را به یک برنامهی ASP.NET Core معرفی کنیم:
namespace CoreIocSample02 { public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddTransient<IMyDisposableService, MyDisposableService>();
namespace CoreIocSample02.Controllers { public class HomeController : Controller { private readonly IMyDisposableService _myDisposableService; public HomeController(IMyDisposableService myDisposableService) { _myDisposableService = myDisposableService; } public IActionResult Index() { _myDisposableService.Run(); return View(); }
در ادامه با اجرای برنامه، اگر به لاگهای آن دقت کنیم، این خروجی قابل مشاهده خواهد بود:
info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] Route matched with {action = "Index", controller = "Home"}. Executing action CoreIocSample02.Controllers.HomeController.Index (CoreIocSample02) info: CoreIocServices.MyDisposableService[0] + MyDisposableService was created . . . info: Microsoft.AspNetCore.Routing.EndpointMiddleware[1] Executed endpoint 'CoreIocSample02.Controllers.HomeController.Index (CoreIocSample02)' info: CoreIocServices.MyDisposableService[0] - MyDisposableService was disposed! info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] Request finished in 1316.4719ms 200 text/html; charset=utf-8
بنابراین IoC Container، به صورت خودکار، کار Dispose این سرویس IDisposable را نیز انجام دادهاست.
Dispose خودکار وهلههایی که توسط IoC Container ایجاد نشدهاند
اگر ایجاد اشیاء از نوع IDisposable را خودتان و خارج از دید IoC Container توکار ASP.NET Core انجام میدهید، از مزیت پاکسازی خودکار منابع توسط آنها در پایان درخواست محروم خواهید شد، اما ... برای رفع این مشکل نیز متد context.Response.RegisterForDispose پیش بینی شدهاست. اگر شیءای از نوع IDisposable را توسط این متد به ASP.NET Core معرفی کنید، در پایان درخواست به صورت خودکار Dispose خواهد شد.
یک مثال: فرض کنید یک StreamWriter را داخل یک میانافزار ایجاد کردهاید، اما آنرا Dispose نکردهاید:
namespace CoreIocSample02 { public class Startup { public void Configure(IApplicationBuilder app, IHostingEnvironment env) { app.Use(async (context, next) => { var writer = File.CreateText(Path.GetTempFileName()); context.Response.RegisterForDispose(writer); context.Items["filewriter"] = writer; await writer.WriteLineAsync("some important information"); await writer.FlushAsync(); await next(); });
اکنون در ادامه، در اکشن متد WriteLog یک کنترلر دلخواه، کار ثبت وقایع با دریافت این writer از HttpContext.Items قابل انجام است؛ چون هنوز طول عمر درخواست جاری پایان نیافته و شیء writer به صورت خودکار Dispose نشدهاست:
namespace CoreIocSample02.Controllers { public class HomeController : Controller { public async Task<IActionResult> WriteLog() { var writer = HttpContext.Items["filewriter"] as StreamWriter; if (writer != null) { await writer.WriteLineAsync("more important information"); await writer.FlushAsync(); } return View(); }
زمانیکه به صورت متداولی از سیستم تزریق وابستگیهای ASP.NET Core استفاده میکنیم، به ازای هر درخواست HTTP رسیده، یک Scope از نوع IServiceScopeFactory ایجاد میشود و با پایان درخواست، این Scope نیز Dispose خواهد شد. به این ترتیب هر سرویس ایجاد شدهی درون این Scope نیز Dispose میشود؛ کاری شبیه به عملیات زیر:
using(var scope = serviceProvider.CreateScope()) { var provider = scope.ServiceProvider; var resolvedService = provider.GetRequiredService(someType); // Use resolvedService... }
مشکل! اگر از سرویس فرضی IOperationScoped با طول عمر Scoped در متدهای مختلف کلاس آغازین برنامه استفاده کنیم (مانند DbContext برنامه)، طول عمری را که دریافت خواهیم کرد singleton خواهد بود و نه Scoped؛ چون درون یک scopeFactory.CreateScope ایجاد شدهی به صورت خودکار توسط یک درخواست قرار نداریم. بنابراین هر درخواست وهلهای از سرویس IOperationScoped با طول عمر Scoped، تنها همان وهلهی ابتدایی آنرا باز میگرداند و singleton رفتار میکند؛ چون scope ایی ایجاد و تخریب نشدهاست.
در یک چنین مواردی، برای اطمینان حاصل کردن از dispose شدن سرویس در پایان کار، نیاز است مراحل ایجاد scope و dispose آنرا به صورت دستی به نحو ذیل مدیریت کنیم:
public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory, IServiceScopeFactory scopeFactory) { using (var scope = scopeFactory.CreateScope()) { var initializer = scope.ServiceProvider.GetService<IOperationScoped>(); initializer.SeedAsync().Wait(); } }
Dispose کردن سرویسهای IDisposable در برنامههای Console
اگر همین سرویس IMyDisposableService را در مثال برنامهی کنسول قسمت اول استفاده کنیم:
var myDisposableService = serviceProvider.GetService<IMyDisposableService>(); myDisposableService.Run();
در برنامهی کنسول، کار ایجاد serviceProvider را خودمان انجام دادیم:
var serviceCollection = new ServiceCollection(); ConfigureServices(serviceCollection); var serviceProvider = serviceCollection.BuildServiceProvider();
namespace Microsoft.Extensions.DependencyInjection { public sealed class ServiceProvider : IServiceProvider, IDisposable, IServiceProviderEngineCallback { public void Dispose(); public object GetService(Type serviceType); } }
namespace CoreIocSample01 { class Program { static void Main(string[] args) { var serviceCollection = new ServiceCollection(); ConfigureServices(serviceCollection); using (var serviceProvider = serviceCollection.BuildServiceProvider()) { var myDisposableService = serviceProvider.GetService<IMyDisposableService>(); myDisposableService.Run(); var testService = serviceProvider.GetService<ITestService>(); testService.Run(); } }
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید: CoreDependencyInjectionSamples-02.zip
مثال - نمایش درصد پیشرفت عملیات توسط SignalR
نکتهای در مورد نگارشهای مختلف SignalR
اگر برنامه شما قرار است دات نت 4 را پشتیبانی کند، آخرین نگارش SignalR که با آن سازگار است، نگارش 1.1.3 میباشد. بنابراین اگر دستور ذیل را اجرا کنید:
PM> Install-Package Microsoft.AspNet.SignalR
اگر دستور ذیل را اجرا کنید، SiganlR 1.x را نصب میکند که با دات نت 4 به بعد سازگار است:
PM> Install-Package Microsoft.AspNet.SignalR -Version 1.1.3
با اینکار Microsoft.AspNet.SignalR.JS نیز به صورت خودکار نصب میگردد و به این ترتیب کلاینت جاوا اسکریپتی SiganlR نیز در برنامه قابل استفاده خواهد بود.
تنظیمات فایل Global.asax.cs
سطر فراخوانی متد RouteTable.Routes.MapHubs باید در ابتدای متد Application_Start فایل Global.asax.cs قرار گیرد (پیش از هر تنظیم دیگری). تفاوتی هم نمیکند که برنامه وب فرم است یا MVC. به این ترتیب مسیریابیهای SignalR تنظیم شده و مسیر http://localhost/signalr/hubs قابل استفاده خواهد بود.
تنظیمات اسکریپتهای سمت کلاینت مورد نیاز
پس از نصب بسته SignalR، سه اسکریپت ذیل باید به ابتدای صفحه وب اضافه شوند تا کلاینتهای جاوا اسکریپتی SignalR بتوانند با سرور ارتباط برقرار کنند:
<script src="Scripts/jquery-1.6.4.min.js" type="text/javascript"></script> <script src="Scripts/jquery.signalR-1.1.3.min.js" type="text/javascript"></script> <script src="signalr/hubs" type="text/javascript"></script>
تعریف کلاس Hub برنامه
using Microsoft.AspNet.SignalR; namespace WebFormsSample03.Common { public class ProgressHub : Hub { /// <summary> /// این متد استاتیک تعریف شده تا در برنامه به صورت مستقیم قابل استفاده باشد /// یا میشد اصلا این متد تعریف نشود و از همان دریافت زمینه هاب در کنترلر استفاده گردد /// </summary> public static void UpdateProgressBar(int value, string connectionId) { var ctx = GlobalHost.ConnectionManager.GetHubContext<ProgressHub>(); ctx.Clients.Client(connectionId).updateProgressBar(value); //فراخوانی یک متد در سمت کلاینت } } }
البته تعریف این متد در اینجا ضروری نبود. حتی میشد بدنه کلاس هاب را خالی تعریف کرد و متد GetHubContext را مستقیما داخل یک کنترلر فراخوانی نمود.
متد UpdateProgressBar، مقدار value را به تنها یک کلاینت که Id آن مساوی connectionId دریافتی است، ارسال میکند. این کلاینت باید یک callback جاوا اسکریپتی را جهت تامین متد پویای updateProgressBar تدارک ببیند.
کلاس Web API کنترلر دریافت فایلها
فرقی نمیکند که برنامه شما از نوع وب فرم است یا MVC. امکانات Web API در هر دو نوع پروژه، قابل دسترسی است (همان ایده یک ASP.NET واحد).
بنابراین نیاز است یک کنترلر وب API جدید را به پروژه اضافه کرده و محتوای آن را به شکل ذیل تغییر دهیم:
using System.Threading; using System.Web.Http; using WebFormsSample03.Common; namespace WebFormsSample03 { public class DownloadRequest { public string Url { set; get; } public string ConnectionId { set; get; } } public class DownloaderController : ApiController { public void Post([FromBody]DownloadRequest data) { //todo: start downloading the data.Url .... ProgressHub.UpdateProgressBar(10, data.ConnectionId); Thread.Sleep(2000); ProgressHub.UpdateProgressBar(40, data.ConnectionId); Thread.Sleep(3000); ProgressHub.UpdateProgressBar(64, data.ConnectionId); Thread.Sleep(2000); ProgressHub.UpdateProgressBar(77, data.ConnectionId); Thread.Sleep(2000); ProgressHub.UpdateProgressBar(92, data.ConnectionId); Thread.Sleep(3000); ProgressHub.UpdateProgressBar(99, data.ConnectionId); Thread.Sleep(2000); ProgressHub.UpdateProgressBar(100, data.ConnectionId); } } }
using System; using System.Web.Http; using System.Web.Routing; namespace WebFormsSample03 { public class Global : System.Web.HttpApplication { protected void Application_Start(object sender, EventArgs e) { // Register the default hubs route: ~/signalr RouteTable.Routes.MapHubs(); RouteTable.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); } } }
همچنین در اینجا با توجه به مسیریابی تعریف شده، باید اطلاعات را به آدرس api/Downloader از نوع Post ارسال کرد.
تعریف کلاینت متصل به Hub
در سمت سرور، متد پویای updateProgressBar فراخوانی شده است. اکنون باید این متد را در سمت کلاینت پیاده سازی کنیم:
<form id="form1" runat="server"> <div> <input id="txtUrl" value="http://www.site.com/file.rar" type="text" /> <input id="send" type="button" value="start download ..." /> <br /> <div id="bar" style="border: #000 1px solid; width:300px;"></div> </div> </form> <script type="text/javascript"> $(function () { $.connection.hub.logging = true; //اطلاعات بیشتری را در جاوا اسکریپت کنسول مرورگر لاگ میکند var progressHub = $.connection.progressHub; //این نام مستعار پیشتر توسط ویژگی نام هاب تنظیم شده است progressHub.client.updateProgressBar = function (value) { //متدی که در اینجا تعریف شده دقیقا مطابق نام متد پویایی است که در هاب تعریف شده است //به این ترتیب سرور میتواند کلاینت را فراخوانی کند $("#bar").html(GaugeBar.generate(value)); }; $.connection.hub.start() // فاز اولیه ارتباط را آغاز میکند .done(function () { $("#send").click(function () { $("#send").attr('disabled', 'disabled'); var myClientId = $.connection.hub.id; // اکنون اتصال برقرار است به سرور $.ajax({ type: "POST", contentType: "application/json", url: "/api/Downloader", data: JSON.stringify({ Url: $("#txtUrl").val(), ConnectionId: myClientId }) }).success(function () { $("#send").removeAttr('disabled'); }).fail(function () { // }); }); }); }); </script>
در ابتدای کار صفحه، اتصال به progressHub برقرار میشود. اگر دقت کنید، نام این هاب با حروف کوچک در اینجا (در سمت کلاینت) آغاز میگردد.
سپس با تعریف یک callback به نام progressHub.client.updateProgressBar، پیامهای دریافتی از طرف سرور را به یک افزونه progress bar جیکوئری، برای نمایش ارسال میکند.
کار اتصال به رویداد کلیک دکمهی آغاز دریافت فایل، در متد done باید انجام شود. این callback زمانی فراخوانی میگردد که کار اتصال به سرور با موفقیت صورت گرفته باشد.
سپس در ادامه توسط jQuery Ajax، اطلاعات Url و همچنین Id کلاینت را به مسیر api/Downloader یا همان web api controller ارسال میکنیم.
کدهای کامل این مثال را از اینجا نیز میتوانید دریافت نمائید:
WebFormsSample03.zip
شروع به کار با RavenDB
با سلام و احترام
بیان کردید "البته لازم به ذکر است که RavenDB در 4 حالت برنامه کنسول (همین سرور فوق)، نصب به عنوان یک سرویس ویندوز NT، هاست شدن در IIS و حالت مدفون شده یا Embedded قابل استفاده است.
" خواهش میکنم راجع به هر مورد توضیح دهید.