string name = "Vahid", country = "Iran"; string jsonString = $$""" { "Name": {{name}}, "Country": {{country}} } """; Console.WriteLine(jsonString);
"{\r\n \"Name\": Vahid,\r\n \"Country\": Iran\r\n}"
و یا امکان درج سادهتر عبارات XML، بدون نیاز به escape قسمتی از آن:
string html = """ <body style="normal"> <div class="book-content"> This is information about the "C# 11" book. </body> <footer> This is information about the author of the "C# 11" book. </footer> </element> """;
سؤال: اگر در محتوای Raw String Literal تعریف شده، نیاز به سه عدد " متوالی وجود داشت، چه باید کرد؟
اگر به توضیحات فوق دقت کنید، عنوان شد که شروع و پایان Raw String Literals، با «حداقل» سه عدد " همراه است و به این صورت پیاده سازی میشود:
- اگر رشتهی شما نیاز به سه "" متوالی داشت، باید raw string literal را با حداقل چهار " شروع و خاتمه دهید.
- اگر رشتهی شما نیاز به چهار "" متوالی داشت، باید raw string literal را با حداقل پنج " شروع و خاتمه دهید.
و ... به همین ترتیب برای مابقی حالات.
var text1 = """" Hello World! """ """"; var text2 = """"" Hello World! """" """"";
کاهش فواصل سمت چپ رشتهها، با استفاده از Raw string literals
به مثال زیر که یک قطعه چند سطری SQL را به دو روش قدیم و جدید تعریف میکند، دقت کنید (فواصل سمت چپ در این مثال مهم هستند):
var sql1 = @" SELECT Name FROM Customers WHERE id = @id "; Console.WriteLine(sql1); var sql2 = """ SELECT Name FROM Customers WHERE id = @id """; Console.WriteLine(sql2);
پاسخ: خیر! خروجی نمایشی این دو به صورت زیر است:
SELECT Name FROM Customers WHERE id = @id SELECT Name FROM Customers WHERE id = @id
خروجی واقعی رشتهی sql1 به صورت زیر است:
"\r\n SELECT Name\r\n FROM Customers\r\n WHERE id = @id\r\n "
"SELECT Name\r\nFROM Customers\r\nWHERE id = @id"
استفاده از string interpolation در raw string literals
همیشه عبارات ما از رشتههای ثابت تشکیل نمیشوند. در این بین همواره نیاز به interpolation با مقادیر ثابت و یا متغیرها وجود خواهد داشت. انجام چنین کاری با raw string literals هم میسر است:
var name = "Vahid"; var text = $""" Hello {name1}! Welcome to the Team! """;
سؤال: اگر در این مثال نیاز بود تا از {} جهت مقاصد نمایشی استفاده شود، چه باید کرد؟ یعنی نیاز به درج {} صرفا متنی وجود داشت.
پاسخ: در اینجا تنها کاری را که باید انجام داد، استفاده از یک $ اضافهتر در ابتدای interpolated string است (مانند مثال ابتدای بحث):
var name1 = "Vahid"; var text = $$""" Hello {{{name1}}}! Welcome to the Team! """;
"Hello {Vahid}!\r\n\r\nWelcome to the Team!"
سؤال: اگر در این مثال نیاز بود تا از {{{}}} جهت مقاصد نمایشی استفاده شود، چه باید کرد؟
پاسخ: باید تعداد $های ابتدای رشته را افزایش داد. برای مثال اگر نیاز به درج سه { متوالی است، باید از 4 عدد $ در ابتدای رشته استفاده کرد:
var name4 = "Vahid"; var text4 = $$$$""" Hello {{{{{{{name4}}}}}}}! Welcome to the Team! """;
"Hello {{{Vahid}}}!\r\nWelcome to the Team!"
string name = "Vahid"; var text1 = $"{name}"; var text2 = $"""{name}"""; var text3 = $$"""{{name}}"""; var text4 = $$$"""{{{name}}}"""; var text5 = $$$$"""{{{{name}}}}""";
اما در مثال زیر، تعداد $ها با تعداد {}ها یکی نیست؛ برای مثال اگر از دو $ ابتدایی استفاده شود، ذکر یک جفت تکی {}، تنها به معنای وجود کاراکتری آنها است و همینطور اگر از سه $ ابتدایی استفاده شود، ذکر یک جفت دو تایی {{}}، تنها به معنای وجود کاراکتری آنها است و همینطور الی آخر. اما اگر معادل تعداد $ها، از جفتهای {} استفاده شود، تمام آنها با هم فقط یک interpolation expression delimiter را تشکیل میدهند.
string name = "Vahid"; var text6 = $$"""{name}"""; // = "{name}" var text7 = $$"""{{{name}}}"""; // = "{Vahid}" var text8 = $$$"""{{name}}"""; // = "{{name}}" var text9 = $$$"""{{{{{name}}}}}"""; // = "{{Vahid}}" var text10 = $$$$"""{{{name}}}"""; // = "{{{name}}}" var text11 = $$$$"""{{{{{{{name}}}}}}}"""; // = "{{{Vahid}}}"
مدیریت سفارشی سطوح دسترسی کاربران در MVC
من فکر میکنم باید حالتهای دیگه ای رو هم بررسی کنی. مثلاً اینکه تغییر در نقش کاربر بوده یا مجوزهای نقش. اگر کاربر در حین پر کردن یک فرم AJAXیی مجوزش گرفته شد چه واکنشی باید رخ بده؟ آیا تمامی این بررسیها و پیاده سازی اونها، به ایجاد یک ارتباط ساده و واکشی مجوز جاری کاربر برای درخواست برتری دارند؟
اگر امکانش رو داری، یک بررسی زمانی بین اون سیستم مدیریت Cache (با در نظر گرفتن تمامی حالت ها) و ایجاد ارتباط ساده با پایگاه داده برای بررسی داشتن مجوز کاربر انجام بده.
بررسی جامع فایلهای Config در دات نت
Thread.CurrentThread.CurrentUICulture = new CultureInfo("fa-IR"); Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture("fa-IR");
[Required(ErrorMessageResourceName = "ResourceKeyName", ErrorMessageResourceType = typeof(<SolutionName>.Resources.<ResourceClassName>))]
public class LocalizationDisplayNameAttribute : DisplayNameAttribute { private readonly DisplayAttribute _display; public LocalizationDisplayNameAttribute(string resourceName, Type resourceType) { _display = new DisplayAttribute { ResourceType = resourceType, Name = resourceName }; } public override string DisplayName { get { try { return _display.GetName(); } catch (Exception) { return _display.Name; } } } }
public class LocalizationDisplayNameAttribute : DisplayNameAttribute { private readonly PropertyInfo nameProperty; public LocalizationDisplayNameAttribute(string displayNameKey, Type resourceType = null) : base(displayNameKey) { if (resourceType != null) nameProperty = resourceType.GetProperty(base.DisplayName, BindingFlags.Static | BindingFlags.Public); } public override string DisplayName { get { if (nameProperty == null) base.DisplayName; return (string)nameProperty.GetValue(nameProperty.DeclaringType, null); } } }
[LocalizationDisplayName("ResourceKeyName", typeof(<SolutionName>.Resources.<ResourceClassName>))]
public class BaseController : Controller { private const string LanguageCookieName = "MyLanguageCookieName"; protected override void ExecuteCore() { var cookie = HttpContext.Request.Cookies[LanguageCookieName]; string lang; if (cookie != null) { lang = cookie.Value; } else { lang = ConfigurationManager.AppSettings["DefaultCulture"] ?? "fa-IR"; var httpCookie = new HttpCookie(LanguageCookieName, lang) { Expires = DateTime.Now.AddYears(1) }; HttpContext.Response.SetCookie(httpCookie); } Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture(lang); base.ExecuteCore(); } }
public class LocalizationActionFilterAttribute : ActionFilterAttribute { private const string LanguageCookieName = "MyLanguageCookieName"; public override void OnActionExecuting(ActionExecutingContext filterContext) { var cookie = filterContext.HttpContext.Request.Cookies[LanguageCookieName]; string lang; if (cookie != null) { lang = cookie.Value; } else { lang = ConfigurationManager.AppSettings["DefaultCulture"] ?? "fa-IR"; var httpCookie = new HttpCookie(LanguageCookieName, lang) { Expires = DateTime.Now.AddYears(1) }; filterContext.HttpContext.Response.SetCookie(httpCookie); } Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture(lang); base.OnActionExecuting(filterContext); } }
<select id="langs" onchange="languageChanged()"> <option value="fa-IR">فارسی</option> <option value="en-US">انگلیسی</option> </select> <script type="text/javascript"> function languageChanged() { setCookie("MyLanguageCookieName", $('#langs').val(), 365); window.location.reload(); } document.ready = function () { $('#langs').val(getCookie("MyLanguageCookieName")); }; function setCookie(name, value, exdays, path) { var exdate = new Date(); exdate.setDate(exdate.getDate() + exdays); var newValue = escape(value) + ((exdays == null) ? "" : "; expires=" + exdate.toUTCString()) + ((path == null) ? "" : "; path=" + path) ; document.cookie = name + "=" + newValue; } function getCookie(name) { var i, x, y, cookies = document.cookie.split(";"); for (i = 0; i < cookies.length; i++) { x = cookies[i].substr(0, cookies[i].indexOf("=")); y = cookies[i].substr(cookies[i].indexOf("=") + 1); x = x.replace(/^\s+|\s+$/g, ""); if (x == name) { return unescape(y); } } } </script>
GET https://www.dntips.ir HTTP/1.1 ... Accept-Language: fa-IR,en-US;q=0.5 ...
Accept-Language: fa-IR,fa;q=0.8,en-US;q=0.5,ar-BH;q=0.3
<system.web> <globalization enableClientBasedCulture="true" uiCulture="auto" culture="auto"></globalization> </system.web>
var langs = filterContext.HttpContext.Request.UserLanguages;
routes.MapRoute( "Localization", // Route name "{lang}/{controller}/{action}/{id}", // URL with parameters new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults );
public class BaseController : Controller { protected override void ExecuteCore() { var lang = RouteData.Values["lang"]; if (lang != null && !string.IsNullOrWhiteSpace(lang.ToString())) { Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture(lang.ToString()); } base.ExecuteCore(); } }
public class BaseController : Controller { protected override void OnActionExecuted(ActionExecutedContext context) { var view = context.Result as ViewResultBase; if (view == null) return; // not a view var viewName = view.ViewName; view.ViewName = GetGlobalizationViewName(viewName, context); base.OnActionExecuted(context); } private static string GetGlobalizationViewName(string viewName, ControllerContext context) { var cultureName = Thread.CurrentThread.CurrentUICulture.Name; if (cultureName == "en-US") return viewName; // default culture if (string.IsNullOrEmpty(viewName)) return context.RouteData.Values["action"] + "." + cultureName; // "Index.fa" int i; if ((i = viewName.IndexOf('.')) > 0) // ex: Index.cshtml return viewName.Substring(0, i + 1) + cultureName + viewName.Substring(i); // "Index.fa.cshtml" return viewName + "." + cultureName; // "Index" ==> "Index.fa" } }
public sealed class RazorGlobalizationViewEngine : RazorViewEngine { protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath) { return base.CreatePartialView(controllerContext, GetGlobalizationViewPath(controllerContext, partialPath)); } protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath) { return base.CreateView(controllerContext, GetGlobalizationViewPath(controllerContext, viewPath), masterPath); } private static string GetGlobalizationViewPath(ControllerContext controllerContext, string viewPath) { //var controllerName = controllerContext.RouteData.GetRequiredString("controller"); var request = controllerContext.HttpContext.Request; var lang = request.Cookies["MyLanguageCookie"]; if (lang != null && !string.IsNullOrEmpty(lang.Value) && lang.Value != "en-US") { var localizedViewPath = Regex.Replace(viewPath, "^~/Views/", string.Format("~/Views/Globalization/{0}/", lang.Value)); if (File.Exists(request.MapPath(localizedViewPath))) viewPath = localizedViewPath; } return viewPath; }
protected void Application_Start() { ViewEngines.Engines.Clear(); ViewEngines.Engines.Add(new RazorGlobalizationViewEngine()); }
<?xml version="1.0" encoding="utf-8"?> <root> <!-- Microsoft ResX Schema ... --> <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> ... </xsd:schema> <resheader name="resmimetype"> <value>text/microsoft-resx</value> </resheader> <resheader name="version"> <value>2.0</value> </resheader> <resheader name="reader"> <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> </resheader> <resheader name="writer"> <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> </resheader> <data name="RightToLeft" xml:space="preserve"> <value>false</value> <comment>RightToleft is false in English!</comment> </data> </root>
EF Code First #15
EF Code first و بانکهای اطلاعاتی متفاوت
در آخرین قسمت از سری EF Code first بد نیست نحوه استفاده از بانکهای اطلاعاتی دیگری را بجز SQL Server نیز بررسی کنیم. در اینجا کلاسهای مدل و کدهای مورد استفاده نیز همانند قسمت 14 است و تنها به ذکر تفاوتها و نکات مرتبط اکتفاء خواهد شد.
حالت کلی پشتیبانی از بانکهای اطلاعاتی مختلف توسط EF Code first
EF Code first با کلیه پروایدرهای تهیه شده برای ADO.NET 3.5 که پشتیبانی از EF را لحاظ کرده باشند، به خوبی کار میکند. پروایدرهای مخصوص ADO.NET 4.0، تنها سه گزینه DeleteDatabase/CreateDatabase/DatabaseExists را نسبت به نگارش قبلی بیشتر دارند و EF Code first ویژگیهای بیشتری را طلب نمیکند.
بنابراین اگر حین استفاده از پروایدر ADO.NET مخصوص بانک اطلاعاتی خاصی با پیغام «CreateDatabase is not supported by the provider» مواجه شدید، به این معنا است که این پروایدر برای دات نت 4 به روز نشده است. اما به این معنا نیست که با EF Code first کار نمیکند. فقط باید یک دیتابیس خالی از پیش تهیه شده را به برنامه معرفی کنید تا مباحث Database Migrations به خوبی کار کنند؛ یا اینکه کلا میتوانید Database Migrations را خاموش کرده (متد Database.SetInitializer را با پارامتر نال فراخوانی کنید) و فیلدها و جداول را دستی ایجاد کنید.
استفاده از EF Code first با SQLite
برای استفاده از SQLite در دات نت ابتدا نیاز به پروایدر ADO.NET آن است: «مکان دریافت درایورهای جدید SQLite مخصوص دات نت»
ضمن اینکه به نکته «استفاده از اسمبلیهای دات نت 2 در یک پروژه دات نت 4» نیز باید دقت داشت.
و یکی از بهترین management studio هایی که برای آن تهیه شده: «SQLite Manager»
پس از دریافت پروایدر آن، ارجاعی را به اسمبلی System.Data.SQLite.dll به برنامه اضافه کنید.
سپس فایل کانفیگ برنامه را به نحو زیر تغییر دهید:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=4.3.1.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
</configSections>
<startup useLegacyV2RuntimeActivationPolicy="true">
<supportedRuntime version="v4.0"/>
</startup>
<connectionStrings>
<clear/>
<add name="Sample09Context"
connectionString="Data Source=CodeFirst.db"
providerName="System.Data.SQLite"/>
</connectionStrings>
</configuration>
همانطور که ملاحظه میکنید، تفاوت آن با قبل، تغییر connectionString و providerName است.
اکنون اگر همان برنامه قسمت قبل را اجرا کنیم به خطای زیر برخواهیم خورد:
«The given key was not present in the dictionary»
در این مورد هم توضیح داده شد. سه گزینه DeleteDatabase/CreateDatabase/DatabaseExists در پروایدر جاری SQLite برای دات نت وجود ندارد. به همین جهت نیاز است فایل «CodeFirst.db» ذکر شده در کانکشن استرینگ را ابتدا دستی درست کرد.
برای مثال از افزونه SQLite Manager استفاده کنید. ابتدا یک بانک اطلاعاتی خالی را درست کرده و سپس دستورات زیر را بر روی بانک اطلاعاتی اجرا کنید تا دو جدول خالی را ایجاد کند (در برگه Execute sql افزونه SQLite Manager):
CREATE TABLE [Payees](
[Id] [integer] PRIMARY KEY AUTOINCREMENT NOT NULL,
[Name] [text] NULL,
[CreatedOn] [datetime] NOT NULL,
[CreatedBy] [text] NULL,
[ModifiedOn] [datetime] NOT NULL,
[ModifiedBy] [text] NULL
);
CREATE TABLE [Bills](
[Id] [integer] PRIMARY KEY AUTOINCREMENT NOT NULL,
[Amount] [float](18, 2) NOT NULL,
[Description] [text] NULL,
[CreatedOn] [datetime] NOT NULL,
[CreatedBy] [text] NULL,
[ModifiedOn] [datetime] NOT NULL,
[ModifiedBy] [text] NULL,
[Payee_Id] [integer] NULL
);
سپس سطر زیر را نیز به ابتدای برنامه اضافه کنید:
Database.SetInitializer<Sample09Context>(null);
به این ترتیب database migrations خاموش میشود و اکنون برنامه بدون مشکل کار خواهد کرد.
فقط باید به یک سری نکات مانند نوع دادهها در بانکهای اطلاعاتی مختلف دقت داشت. برای مثال integer در اینجا از نوع Int64 است؛ بنابراین در برنامه نیز باید به همین ترتیب تعریف شود تا نگاشتها به درستی انجام شوند.
در کل تنها مشکل پروایدر فعلی SQLite عدم پشتیبانی از مباحث database migrations است. این مورد را خاموش کرده و تغییرات ساختار بانک اطلاعاتی را به صورت دستی به بانک اطلاعاتی اعمال کنید. بدون مشکل کار خواهد کرد.
البته اگر به دنبال پروایدری تجاری با پشتیبانی از آخرین نگارش EF Code first هستید، گزینه زیر نیز مهیا است:
http://devart.com/dotconnect/sqlite/
برای مثال اگر علاقمند به استفاده از حالت تشکیل بانک اطلاعاتی SQLite در حافظه هستید (با رشته اتصالی ویژه Data Source=:memory:;Version=3;New=True;)، فعلا تنها گزینه مهیا استفاده از پروایدر تجاری فوق است؛ زیرا مبحث Database Migrations را به خوبی پشتیبانی میکند.
استفاده از EF Code first با SQL Server CE
قبلا در مورد «استفاده از SQL-CE به کمک NHibernate» مطلبی را در این سایت مطالعه کردهاید. سه مورد اول آن با EF Code first یکی است و تفاوتی نمیکند (یک سری بحث عمومی مشترک است). البته با یک تفاوت؛ در اینجا EF Code first قادر است یک بانک اطلاعاتی خالی SQL Server CE را به صورت خودکار ایجاد کند و نیازی نیست تا آنرا دستی ایجاد کرد. مباحث database migrations و به روز رسانی خودکار ساختار بانک اطلاعاتی نیز در اینجا پشتیبانی میشود.
برای استفاده از آن ابتدا ارجاعی را به اسمبلی System.Data.SqlServerCe.dll قرار گرفته در مسیر Program Files\Microsoft SQL Server Compact Edition\v4.0\Desktop اضافه کنید.
سپس رشته اتصالی به بانک اطلاعاتی و providerName را به نحو زیر تغییر دهید:
<connectionStrings>
<clear/>
<add name="Sample09Context"
connectionString="Data Source=mydb.sdf;Password=1234;Encrypt Database=True"
providerName="System.Data.SqlServerCE.4.0"/>
</connectionStrings>
بدون نیاز به هیچگونه تغییری در کدهای برنامه، همین مقدار تغییر در تنظیمات ابتدایی برنامه برای کار با SQL Server CE کافی است.
ضمنا مشکلی هم با فیلد Identity در آخرین نگارش EF Code first وجود ندارد؛ برخلاف حالت database first آن که پیشتر این اجازه را نمیداد و خطای «Server-generated keys and server-generated values are not supported by SQL Server Compact» را ظاهر میکرد.
استفاده از EF Code first با MySQL
برای استفاده از EF Code first با MySQL (نگارش 5 به بعد البته) ابتدا نیاز است پروایدر مخصوص ADO.NET آنرا دریافت کرد: (^)
که از EF نیز پشتیبانی میکند. پس از نصب آن، ارجاعی را به اسمبلی MySql.Data.dll قرار گرفته در مسیر Program Files\MySQL\MySQL Connector Net 6.5.4\Assemblies\v4.0 به پروژه اضافه نمائید.
سپس رشته اتصالی و providerName را به نحو زیر تغییر دهید:
<connectionStrings>
<clear/>
<add name="Sample09Context"
connectionString="Datasource=localhost; Database=testdb2; Uid=root; Pwd=123;"
providerName="MySql.Data.MySqlClient"/>
</connectionStrings>
<system.data>
<DbProviderFactories>
<remove invariant="MySql.Data.MySqlClient"/>
<add name="MySQL Data Provider"
invariant="MySql.Data.MySqlClient"
description=".Net Framework Data Provider for MySQL"
type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=6.5.4.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" />
</DbProviderFactories>
</system.data>
همانطور که مشاهده میکنید در اینجا شماره نگارش دقیق پروایدر مورد استفاده نیز ذکر شده است. برای مثال اگر چندین پروایدر روی سیستم نصب است، با مقدار دهی DbProviderFactories میتوان از نگارش مخصوصی استفاده کرد.
با این تغییرات پس از اجرای برنامه قسمت قبل، به خطای زیر برخواهیم خورد:
The given key was not present in the dictionary
توضیحات این مورد با قسمت SQLite یکی است؛ به عبارتی نیاز است بانک اطلاعاتی testdb را دستی درست کرد. همچنین جداول و فیلدها را نیز باید دستی ایجاد کرد و database migrations را نیز باید خاموش کرد (پارامتر Database.SetInitializer را به نال مقدار دهی کنید).
برای این منظور یک دیتابیس خالی را ایجاد کرده و سپس دو جدول زیر را به آن اضافه کنید:
CREATE TABLE IF NOT EXISTS `bills` (
`Id` int(11) NOT NULL AUTO_INCREMENT,
`Amount` float DEFAULT NULL,
`Description` varchar(400) CHARACTER SET utf8 COLLATE utf8_persian_ci NOT NULL,
`CreatedOn` datetime NOT NULL,
`CreatedBy` varchar(400) CHARACTER SET utf8 COLLATE utf8_persian_ci NOT NULL,
`ModifiedOn` datetime NOT NULL,
`ModifiedBy` varchar(400) CHARACTER SET utf8 COLLATE utf8_persian_ci NOT NULL,
`Payee_Id` int(11) NOT NULL,
PRIMARY KEY (`Id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_persian_ci AUTO_INCREMENT=1 ;
CREATE TABLE IF NOT EXISTS `payees` (
`Id` int(11) NOT NULL AUTO_INCREMENT,
`Name` varchar(400) CHARACTER SET utf8 COLLATE utf8_persian_ci NOT NULL,
`CreatedOn` datetime NOT NULL,
`CreatedBy` varchar(400) CHARACTER SET utf8 COLLATE utf8_persian_ci NOT NULL,
`ModifiedOn` datetime NOT NULL,
`ModifiedBy` varchar(400) CHARACTER SET utf8 COLLATE utf8_persian_ci NOT NULL,
PRIMARY KEY (`Id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_persian_ci AUTO_INCREMENT=1 ;
پس از این تغییرات، برنامه بدون مشکل اجرا خواهد شد (ایجاد بانک اطلاعاتی خالی به همراه ایجاد ساختار جداول و خاموش کردن database migrations که توسط این پروایدر پشتیبانی نمیشود).
به علاوه پروایدر تجاری دیگری هم در سایت devart.com برای MySQL و EF Code first مهیا است که مباحث database migrations را به خوبی مدیریت میکند.
مشکل!
اگر به همین نحو برنامه را اجرا کنیم، فیلدهای یونیکد فارسی ثبت شده در MySQL با «??????? ?? ????» مقدار دهی خواهند شد و تنظیم CHARACTER SET utf8 COLLATE utf8_persian_ci نیز کافی نبوده است (این مورد با SQLite یا نگارشهای مختلف SQL Server بدون مشکل کار میکند و نیاز به تنظیم اضافهتری ندارد):
ALTER TABLE `bills` DEFAULT CHARACTER SET utf8 COLLATE utf8_persian_ci
برای رفع این مشکل توصیه شده است که CharSet=UTF8 را به رشته اتصالی به بانک اطلاعاتی اضافه کنیم. اما در این حالت خطای زیر ظاهر میشود:
The provider did not return a ProviderManifestToken string
این مورد فقط به اشتباه بودن تعاریف رشته اتصالی بر میگردد؛ یا عدم پشتیبانی از تنظیم اضافهای که در رشته اتصالی ذکر شده است.
مقدار صحیح آن دقیقا مساوی CHARSET=utf8 است (با همین نگارش و رعایت کوچکی و بزرگی حروف؛ مهم!):
<connectionStrings>
<clear/>
<add name="Sample09Context"
connectionString="Datasource=localhost; Database=testdb; Uid=root; Pwd=123;CHARSET=utf8"
providerName="MySql.Data.MySqlClient"/>
</connectionStrings>
به این ترتیب، مشکل ثبت عبارات یونیکد فارسی برطرف میشود (البته جدول هم بهتر است به DEFAULT CHARACTER SET utf8 COLLATE utf8_persian_ci تغییر پیدا کند؛ مطابق دستور Alter ایی که در بالا ذکر شد).
شرح #String Interning in C
در این قسمت در ابتدا نحوهی باز کردن یک پایگاه داهی چند بعدی را در محیط BIMS بررسی کرده و سپس چگونگی ساخت یک MDB را از پایه بررسی میکنیم. برای ادامه دادن این قسمت نیاز میباشد که پایگاه دادهی AdventureWorkDW2008 را در SSAS نصب کرده باشید .
در ابتدا مطابق شکل زیر منوی File سپس زیر منوی Open و Analysis Service Database را انتخاب نمایید.
در ادامه میبایست نام Server را مشخص نمایید و دقت داشته باشید که در اینجا منظور از نام سرور، نام سرور SSAS میباشد (در صورتیکه بر روی خود سرور در حال کار میباشید از . به جای نام سرور استفاده کنید). سپس در قسمت Database، نام پایگاه دادهی چند بعدی را انتخاب نمایید. در صورتی که به جز Adventure Work DW 2008 ، پایگاه دادههای چند بعدی دیگری را در SSAS داشته باشید، یک لیست از آنها را مشاهده خواهید کرد و در صورتیکه لیست شما خالی میباشد، احتمال دارد نام سرور اشتباه باشد یا روی سرویس SSAS مربوط به آن سرور هیچ پایگاه دادهی چند بعدی نصب نباشد.
حال مسیری را برای ذخیره سازی پروژهی جدید در نظر بگیرید:
پس از کمی شکیبایی، واکشی اطلاعات از روی پایگاه دادهی چند بعدی انتخاب شده انجام میشود و یک پروژه در ارتباط با آن پایگاه داده ساخته میشود.
همان طور که مشخص میباشد، یک شیء درون شاخهی Data Source وجود دارد که مشخص کنندهی ارتباط این پروژه با پایگاه دادهی Data Warehouse است. برای مشاهدهی این ارتباط، بر روی Adventure Work DW کلیک راست کنید و سپس گزینهی Open را انتخاب نمایید. در ادامه گزینهی Edit را بزنید.
سپس در پنجرهی جدید، تنظیمات رشتهی ارتباطی با DW را مشاهده نمایید
با زدن کلید Test Connection باید پیام Test Connection Succeeded را مشاهده نمایید. اکنون پنجرهها را با زدن کلید OK ببندید.
در قسمت Data Source View سه شی تعریف شده است؛ براساس دسته بندی مورد نظر و جاری در Business موجود در Adventure Work .
با کلیک راست کردن بر روی Adventure Works DW و انتخاب گزینهی Open، اقدام به باز کردن DSV انتخاب شده کنید. در صفحهی باز شده میتوانید انواع دیاگرام تهیه شده را مشاهده نمایید و همچنین لیستی از جداول موجود در این DSV مشخص میباشد.
با کلیک راست کردن در فضای خالی دیاگرام ، امکان Add/Remove کردن جداول را به دیاگرام دارید.
در شکل بالا بعد از انتخاب یک جدول در سمت راست و انتقال آن به سمت چپ میتوانید با زدن دکمهی Add Related Table براساس کلیدهای خارجی، جداول مرتبط با جدول انتخاب شده را به صورت خودکار انتخاب نمایید و به قسمت چپ انتقال دهید.
شما در ساخت Cube مشخص مینمایید که Cube را از کدام DSV خواهید ساخت. بنابراین انتخاب جداول در DSV ها میبایست براساس نوع Business شما باشد تا در ساخت Cube به مشکلی برخورد نکنید.
در ساختار درختی موجود در پنجرهی Solution در شاخهی Cube، میتوانید Adventure Works را باز کنید (کلیک راست و انتخاب Open ) .
در شکل بالا در سمت چپ، میتوانید Measure ها و Dimension های موجود در این Cube را مشاهده کنید. همچنین در قسمت بالا چندین Tab وجود دارند که در هر کدام تنظیمات بیشتری را بر روی Cube اعمال میکنیم. با توجه به اینکه طراحی Cube ها کاری تخصصی میباشد و نیاز به اطلاعات زیادی دارد اجازه دهید مقاله ای در خصوص طراحی Cube در SSAS جداگانه انتشار داده شود و فعلا در همین حد بسنده کنیم. با این حال در صورت نیاز میتوانید برای اطلاعات بیشتر در این خصوص کتاب Microsoft SQL Server Analysis Services 2008 With MDX از انتشارات Wrox را مطالعه نمایید.
در Solution Explorer در شاخهی ،Dimensions میتوانید تمامی بعدهایی که در تمامی Cube های شما استفاده شدهاند را مشاهده نمایید.
با انتخاب یک بعد (ترجیحا بعد Date ) و با کلیک راست کردن و انتخاب گزینهی Open آن را باز نمایید.
در پنجرهی باز شده میتوانید 4 Tab در بالا را مشاهد نمایید و در Tab نخست، Attribute ها و همچنین ساختار Hierarchies و در آخر Data source View را مشاهده نمایید.
در Attribute relationships می توانید ارتباط صفتهای یک بعد را مشخص نمایید.
در Browsing Tab میتوانید محتوای Dimension را بررسی نمایید (البته اگر در پروژهی جدید قرار دارید حتما میبایست پروژه را Deploy کرده باشید. در حالتیکه یک پایگاه داهی چند بعدی را باز میکنید، نیازی به Deploy کردن نمیباشد؛ زیرا حتما قبلا این کار انجام شده است (زیرا شما پایگاه دادهی چند بعدی را بعد از Deploy کردن پروژهی SSAS خواهید داشت ))
در صورتیکه مانند روش بالا یک پایگاه دادهی چند بعدی را باز کنیم، دیگر نیازی به Deploy کردن نمیباشد و فقط برای اعمال تغییرات روی پایگاه دادهی چند بعدی باید پروژه را Process کنیم و برای این منظور روی نام پروژه کلیک راست کرده و گزینهی Process را انتخاب کنید. با این کار تغییرات اعمال شده در BIMS روی پایگاه دادهی SSAS اعمال میگردند و دادهها با توجه به ساختار Cube ها دوباره پردازش میشوند.
برای ساخت یک پروژهی جدید به شکل زیر عمل میکنیم :
در ابتدا BIMS را باز کرده و سپس به منوی File رفته و در قسمت New گزینهی Project را انتخاب میکنیم. سپس در صفحهی باز شده، مطابق شکل زیر عمل کرده و یک پروژه از نوع Analysis Service Multidimensional … میسازیم.
سپس برروی شاخهی Data Source کلیک راست کرده و گزینهی New Data Source را میزنیم و پنجرههای ویزارد را به جلو میرویم.
در ابتدا باید یک Connection به DW تولید کنیم. برای این منظور در پنجرهی فوق دکمهی New را زده و اطلاعات را مطابق شکل زیر پر میکنیم.
و سپس OK را میزنیم.
در صورتی که SSAS در یک سرور دیگر نصب شده است در پنجرهی بعدی نیاز میباشد نام کاربری را که به سرویس SSAS در آن سرور دسترسی دارد را وارد کنیم.
در صورتی که SSAS روی سیستم Local نصب شده است و کاربری که با آن Login هستیم دسترسی کافی به SSAS را دارد، گزینهی Use the credentials of the current user را انتخاب میکنیم.
در صفحهی آخر یک نام برای DS انتخاب میکنیم.
سپس نیاز میباشد یک DSV بسازیم. برای این منظور روی شاخهی Data Source View کلیک راست کرده و گزینهی New را انتخاب کرده و سپس در پنجرهی Wizard باید Data Source ساخته شده در مرحلهی قبل را انتخاب کرده و سپس Next را بزنیم. در اینجا بر اساس بیزینسهای مختلف، راه کارهای گوناگونی را داریم. به عبارت دیگر میتوان جداول Fact و Dimension های مرتبط با آنرا بر اساس زیر سیستمهای مختلف انتخاب کرده و برای هر کدام از آنها یک DSV بسازیم. به نظر من میتوانیم تمامی جداول را در این مرحله انتخاب کرده و سپس این تفکیک بندی را در سطح Cube ها انجام داد. به طور کلی دقت داشته باشید به هیچ عنوان DSV و Cube های سیستم را خیلی تفکیک نکنید. زیرا در نوشتن کوئریها و Join بین Cube ها با مشکل و سختی روبرو خواهید شد. (از لحاظ تجربی تفکیک بندی به شرطی صورت گیرد که نیازی به Join کردن Cube ها در MDX Query ها نباشد.)
سپس یک نام برای DSV خود انتخاب کرده و Finish را بزنید.
خوب؛ آخرین مرحله ساخت Cube میباشد (البته در طراحی Cube مطالب بسیاری وجود دارند که در یک مقالهی دیگر تلاش خواهم کرد تمامی آن موارد را توضیح دهم.)
برای ساخت Cube ، روی شاخهی Cube کلیک راست کرده و گزینهی New را بزنید.
سپس Use Existing Table را انتخاب کرده و Next را بزنید.
در پنجرهی بعدی باید DSV را انتخاب کرد و بعد جداول مورد نیاز در طراحی Cube را انتخاب کنید. فراموش نکنید در صورت انتخاب یک Fact تمامی Dimension های مرتبط با آن را انتخاب نماید. دکمه Next را بزنید.
در پنجرهی بعدی باید جداول Fact را انتخاب کرده و دکمهی Next را بزنید.
سپس در پنجرهی بعدی دایمنشن را انتخاب نمایید. (ترجیحا اجازه بدهید خود BIMS برای شما Dimension ها را بسازد، هرچند که خود شما میتوانید بعدا به صورت دستی Dimension ها را ایجاد کنید).
بعد از زدن دکمهی Next نامی برای Cube خود انتخاب نمایید و سپس دکمهی Finish را بزنید.
بعد از ساخت Cube ، چندین دایمنشن به صورت خودکار ساخته میشوند . البته گاهی نیاز میباشد که اقدام به ساخت ساختارهای سلسله مراتبی در Dimension ها کنیم (این مورد را در یک مقاله جداگانه آموزش خواهم داد.)
پروژه با کلیدهای ترکیبی Ctrl+Shift+B ساخته میشود و بعد از اطمینان از درست بودن ساخت پروژه، آن را باید Deploy کرد.
برای Deploy کردن یک پروژه کافی است بعد از تنظیم کردن رشتهی ارتباطی در DS (قبلا توضیح داده شده است) روی پروژه کلیک راست کرده و گزینهی Deploy را بزنیم.
نگاهی به SignalR Clients
مصرف کنندگان یک Hub میتوانند انواع و اقسام برنامههای کلاینت مانند jQuery Clients و یا حتی یک برنامه کنسول ساده باشند و همچنین Hubهای دیگر نیز قابلیت استفاده از این امکانات Hubهای موجود را دارند. تیم SignalR امکان استفاده از Hubهای آنرا در برنامههای دات نت 4 به بعد، برنامههای WinRT، ویندوز فون 8، سیلورلایت 5، jQuery و همچنین برنامههای CPP نیز مهیا کردهاند. به علاوه گروههای مختلف نیز با توجه به سورس باز بودن این مجموعه، کلاینتهای iOS Native، iOS via Mono و Android via Mono را نیز به این لیست اضافه کردهاند.
بررسی کلاینتهای jQuery
با توجه به پروتکل مبتنی بر JSON سیگنالآر، استفاده از آن در کتابخانههای جاوا اسکریپتی همانند jQuery نیز به سادگی مهیا است. برای نصب آن نیاز است در کنسول پاور شل نوگت، دستور زیر را صادر کنید:
PM> Install-Package Microsoft.AspNet.SignalR.JS
با استفاده از افزونه SignalR jQuery، به دو طریق میتوان به یک Hub اتصال برقرار کرد:
الف) استفاده از فایل proxy تولید شده آن (این فایل، در زمان اجرای برنامه تولید میشود و یا امکان استفاده از آن به کمک ابزارهای کمکی نیز وجود دارد)
نمونهای از آنرا در قسمت قبل ملاحظه کردید؛ همان فایل تولید شده در مسیر /signalr/hubs برنامه. به نوعی به آن Service contract نیز گفته میشود (ارائه متادیتا و قراردادهای کار با یک سرویس Hub). این فایل همانطور که عنوان شد به صورت پویا در زمان اجرای برنامه ایجاد میشود.
امکان تولید آن توسط برنامه کمکی signalr.exe نیز وجود دارد؛ برای دریافت آن میتوان از طریق NuGet اقدام کرد (بسته Microsoft.AspNet.SignalR.Utils) که نهایتا در پوشه packages قرار خواهد گرفت. نحوه استفاده از آن نیز به صورت زیر است:
Signalr.exe ghp http://localhost/
ب) بدون استفاده از فایل proxy و به کمک روش late binding (انقیاد دیر هنگام)
برای کار با یک Hub از طریق jQuery مراحل ذیل باید طی شوند:
1) ارجاعی به Hub باید مشخص شود.
2) روالهای رخدادگردان تنظیم گردند.
3) اتصال به Hub برقرار گردد.
4) متدی فراخوانی شود.
در اینجا باید دقت داشت که امکانات Hub به صورت خواص
$.connection
$.connection.chatHub
خوب، تا اینجا فرض بر این است که یک پروژه خالی ASP.NET را آغاز و سپس فرمان نصب Microsoft.AspNet.SignalR.JS را نیز همانطور که عنوان شد، صادر کردهاید. در ادامه یک فایل ساده html را به نام chat.htm، به این پروژه جدید اضافه کنید (برای استفاده از کتابخانه جاوا اسکریپتی SignalR الزامی به استفاده از صفحات کامل پروژههای وب نیست).
<!DOCTYPE> <html> <head> <title></title> <script src="Scripts/jquery-1.6.4.min.js" type="text/javascript"></script> <script src="Scripts/jquery.signalR-1.0.1.min.js" type="text/javascript"></script> <script src="http://localhost:1072/signalr/hubs" type="text/javascript"></script> </head> <body> <div> <input id="txtMsg" type="text" /><input id="send" type="button" value="send msg" /> <ul id="messages"> </ul> </div> <script type="text/javascript"> $(function () { var chat; $.connection.hub.logging = true; //اطلاعات بیشتری را در جاوا اسکریپت کنسول مرورگر لاگ میکند chat = $.connection.chat; //این نام مستعار پیشتر توسط ویژگی نام هاب تنظیم شده است chat.client.hello = function (message) { //متدی که در اینجا تعریف شده دقیقا مطابق نام متد پویایی است که در هاب تعریف شده است //به این ترتیب سرور میتواند کلاینت را فراخوانی کند $("#messages").append("<li>" + message + "</li>"); }; $.connection.hub.start(/*{ transport: 'longPolling' }*/); // فاز اولیه ارتباط را آغاز میکند $("#send").click(function () { // Hub's `SendMessage` should be camel case here chat.server.sendMessage($("#txtMsg").val()); }); }); </script> </body> </html>
توضیحات:
همانطور که ملاحظه میکنید ابتدا ارجاعاتی به jquery و jquery.signalR-1.0.1.min.js اضافه شدهاند. سپس نیاز است مسیر دقیق فایل پروکسی هاب خود را نیز مشخص کنیم. اینکار با تعریف مسیر signalr/hubs انجام شده است.
<script src="http://localhost:1072/signalr/hubs" type="text/javascript"></script>
سپس ارجاعی به هاب تعریف شده، تعریف گردیده است. اگر از قسمت قبل به خاطر داشته باشید، توسط ویژگی HubName، نام chat را برگزیدیم. بنابراین connection.chat ذکر شده دقیقا به این هاب اشاره میکند.
سپس سطر chat.client.hello مقدار دهی شده است. متد hello، متدی dynamic و تعریف شده در سمت هاب برنامه است. به این ترتیب میتوان به پیامهای رسیده از طرف سرور گوش فرا داد. در اینجا، این پیامها، به li ایی با id مساوی messages اضافه میشوند.
سپس توسط فراخوانی متد connection.hub.start، فاز negotiation شروع میشود. در اینجا حتی میتوان نوع transport را نیز صریحا انتخاب کرد که نمونهای از آن را به صورت کامنت شده جهت آشنایی با نحوه تعریف آن مشاهده میکنید. مقادیر قابل استفاده در آن به شرح زیر هستند:
- webSockets - forverFrame - serverSentEvents - longPolling
اکنون به صورت جداگانه یکبار برنامه hub را در مرورگر باز کنید. سپس بر روی فایل chat.htm کلیک راست کرده و گزینه مشاهده آن را در مرورگر نیز انتخاب نمائید (گزینه View in browser منوی کلیک راست).
خوب! پروژه کار نمیکند! برای اینکه مشکلات را بهتر بتوانید مشاهده کنید نیاز است به JavaScript Console مرورگر خود مراجعه نمائید. برای مثال در مرورگر کروم دکمه F12 را فشرده و برگه Console آنرا باز کنید. در اینجا اعلام میکند که فاز negotiation قابل انجام نیست؛ چون مسیر پیش فرضی را که انتخاب کرده است، همین مسیر پروژه دومی است که اضافه کردهایم (کلاینت ما در پروژه دوم قرار دارد و نه در همان پروژه اول هاب).
برای اینکه مسیر دقیق hub را در این حالت مشخص کنیم، سطر زیر را به ابتدای کدهای جاوا اسکریپتی فوق اضافه نمائید:
$.connection.hub.url = 'http://localhost:1072/signalr'; //چون در یک پروژه دیگر قرار داریم
using System; using System.Web; using System.Web.Routing; using Microsoft.AspNet.SignalR; namespace SignalR02 { public class Global : HttpApplication { protected void Application_Start(object sender, EventArgs e) { // Register the default hubs route: ~/signalr RouteTable.Routes.MapHubs(new HubConfiguration { EnableCrossDomain = true }); } } }
SignalR: Auto detected cross domain url. jquery.signalR-1.0.1.min.js:10 SignalR: Negotiating with 'http://localhost:1072/signalr/negotiate'. jquery.signalR-1.0.1.min.js:10 SignalR: SignalR: Initializing long polling connection with server. jquery.signalR-1.0.1.min.js:10 SignalR: Attempting to connect to 'http://localhost:1072/signalr/connect?transport=longPolling&connectionToken…NRh72omzsPkKqhKw2&connectionData=%5B%7B%22name%22%3A%22chat%22%7D%5D&tid=3' using longPolling. jquery.signalR-1.0.1.min.js:10 SignalR: Longpolling connected jquery.signalR-1.0.1.min.js:10
در برگه شبکه، مطابق شکل فوق، امکان آنالیز اطلاعات رد و بدل شده مهیا است. برای مثال در حالتیکه سرور پیام دریافتی را به کلیه کلاینتها ارسال میکند، نام متد و نام هاب و سایر پارامترها در اطلاعات به فرمت JSON آن به خوبی قابل مشاهده هستند.
یک نکته:
اگر از ویندوز 8 (یعنی IIS8) و VS 2012 استفاده میکنید، برای استفاده از حالت Web socket، ابتدا فایل وب کانفیگ برنامه را باز کرده و در قسمت httpRunTime، مقدار ویژگی targetFramework را بر روی 4.5 تنظیم کنید. اینبار اگر مراحل negotiation را بررسی کنید در همان مرحله اول برقراری اتصال، از روش Web socket استفاده گردیده است.
تمرین 1
به پروژه ساده و ابتدایی فوق یک تکست باکس دیگر به نام Room را اضافه کنید؛ به همراه دکمه join. سپس نکات قسمت قبل را در مورد الحاق به یک گروه و سپس ارسال پیام به اعضای گروه را پیاده سازی نمائید. (تمام نکات آن با مطلب فوق پوشش داده شده است و در اینجا باید صرفا فراخوانی متدهای عمومی دیگری در سمت هاب، صورت گیرد)
تمرین 2
در انتهای قسمت دوم به نحوه ارسال پیام از یک هاب به هابی دیگر اشاره شد. این MonitorHub را ایجاد کرده و همچنین یک کلاینت جاوا اسکریپتی را نیز برای آن تهیه کنید تا بتوان اتصال و قطع اتصال کلیه کاربران سیستم را مانیتور و مشاهده کرد.
پیاده سازی کلاینت jQuery بدون استفاده از کلاس Proxy
در مثال قبل، از پروکسی پویای مهیای در آدرس signalr/hubs استفاده کردیم. در اینجا قصد داریم، بدون استفاده از آن نیز کار برپایی کلاینت را بررسی کنیم.
بنابراین یک فایل جدید html را مثلا به نام chat_np.html به پروژه دوم برنامه اضافه کنید. سپس محتویات آنرا به نحو زیر تغییر دهید:
<!DOCTYPE> <html> <head> <title></title> <script src="Scripts/jquery-1.6.4.min.js" type="text/javascript"></script> <script src="Scripts/jquery.signalR-1.0.1.min.js" type="text/javascript"></script> </head> <body> <div> <input id="txtMsg" type="text" /><input id="send" type="button" value="send msg" /> <ul id="messages"> </ul> </div> <script type="text/javascript"> $(function () { $.connection.hub.logging = true; //اطلاعات بیشتری را در جاوا اسکریپت کنسول مرورگر لاگ میکند var connection = $.hubConnection(); connection.url = 'http://localhost:1072/signalr'; //چون در یک پروژه دیگر قرار داریم var proxy = connection.createHubProxy('chat'); proxy.on('hello', function (message) { //متدی که در اینجا تعریف شده دقیقا مطابق نام متد پویایی است که در هاب تعریف شده است //به این ترتیب سرور میتواند کلاینت را فراخوانی کند $("#messages").append("<li>" + message + "</li>"); }); $("#send").click(function () { // Hub's `SendMessage` should be camel case here proxy.invoke('sendMessage', $("#txtMsg").val()); }); connection.start(); }); </script> </body> </html>
کلاینتهای دات نتی SignalR
تا کنون Solution ما حاوی یک پروژه Hub و یک پروژه وب کلاینت جیکوئری است. به همین Solution، یک پروژه کلاینت کنسول ویندوزی را نیز اضافه کنید.
سپس در خط فرمان پاور شل نوگت دستور زیر را صادر نمائید تا فایلهای مورد نیاز به پروژه کنسول اضافه شوند:
PM> Install-Package Microsoft.AspNet.SignalR.Client
پس از نصب آن اگر به پوشه packages مراجعه کنید، نگارشهای مختلف آنرا مخصوص سیلورلایت، دات نتهای 4 و 4.5، WinRT و ویندوز فون8 نیز میتوانید در پوشه Microsoft.AspNet.SignalR.Client ملاحظه نمائید. البته در ابتدای نصب، انتخاب نگارش مناسب، بر اساس نوع پروژه جاری به صورت خودکار صورت میگیرد.
مدل برنامه نویسی آن نیز بسیار شبیه است به حالت عدم استفاده از پروکسی در حین استفاده از jQuery که در قسمت قبل بررسی گردید و شامل این مراحل است:
1) یک وهله از شیء HubConnection را ایجاد کنید.
2) پروکسی مورد نیاز را جهت اتصال به Hub از طریق متد CreateProxy تهیه کنید.
3) رویدادگردانها را همانند نمونه کدهای جاوا اسکریپتی قسمت قبل، توسط متد On تعریف کنید.
4) به کمک متد Start، اتصال را آغاز نمائید.
5) متدها را به کمک متد Invoke فراخوانی نمائید.
using System; using Microsoft.AspNet.SignalR.Client.Hubs; namespace SignalR02.WinClient { class Program { static void Main(string[] args) { var hubConnection = new HubConnection(url: "http://localhost:1072/signalr"); var chat = hubConnection.CreateHubProxy(hubName: "chat"); chat.On<string>("hello", msg => { Console.WriteLine(msg); }); hubConnection.Start().Wait(); chat.Invoke<string>("sendMessage", "Hello!"); Console.WriteLine("Press a key to terminate the client..."); Console.Read(); } } }
نکته مهم
کلیه فراخوانیهایی که در اینجا ملاحظه میکنید غیرهمزمان هستند.
به همین جهت پس از متد Start، متد Wait ذکر شدهاست تا در این برنامه ساده، پس از برقراری کامل اتصال، کار invoke صورت گیرد و یا زمانیکه callback تعریف شده توسط متد chat.On فراخوانی میشود نیز این فراخوانی غیرهمزمان است و خصوصا اگر نیاز است رابط کاربری برنامه را در این بین به روز کنید باید به نکات به روز رسانی رابط کاربری از طریق یک ترد دیگر دقت داشت.