مسیریابی (Routing) در ASP.NET MVC 5.x
- اگر امکان تبدیل امضای متد را به Task ندارید از روش ذیل استفاده کنید (Task.Run در اینجا اضافی است):
var pages =_pageService.FindAllAsync().GetAwaiter().GetResult();
ASP.NET MVC #21
@Ajax.ActionLink("Test", "action", new AjaxOptions { HttpMethod = "POST", InsertionMode = InsertionMode.Replace, UpdateTargetId = "UserDiv", OnSuccess="$.validator.unobtrusive.parse('#my_form_id');" } )
<script type="text/javascript"> $.validator.unobtrusive.parse("#my_frm_id"); </script>
توابع تعمیم یافته در #C
البته توسعه تعداد توابع نیز به نوعی توسعه توابع است و الزامی ندارد فقط یک تابع توسعه یابد تا تعمیم و توسعه صدق کند
البته الحاق هم صحیح است .
نکته دیگر آنکه برای متد و فانکشن و مانند آن در فارسی معادلی غیر تابع ندیدهام که متداول باشد. بعلاوه آنکه تابع مفهوم همه اینها را شامل میشود خواه نوع بازگشتی داشته باشد یا void باشد، لذا تعبیر غیر صحیحی نیست
باتشکر
در برنامههای وب امروز نیازی به فراخوانی ثوابت که در طول حیات برنامه انگشت شمار تغیر میکنند نیست و با توجه به استفاده از فرامین و متدهای سمت کلاینت احتیاج هست تا این ثوابت بار اول لود صفحه به کلاینت پاس داده شوند.
میتوان در این گونه موارد از قابلیتهای گوناگونی استفاده کرد که در اینجا ما با استفاده از یک فیلد مخفی و json مقدار را به کلاینت پاس میدهیم و در این مثال در سمت کلاینت نیز دراپ دان را با این مقادیر پر میکنیم:
public enum PersistType { Persistable = 1, NotPersist = 2, AlwaysPersist = 3 }
لیست را باید قبل از پر کردن در فیلد مخفی به json بصورت serialize شده تبدیل کرد، برای این منظور از JavaScriptSerializer موجود در اسمبلیهای دات نت در متد زیر استفاده شده:
public static string ConvertEnumToJavascript(Type t) { if (!t.IsEnum) throw new Exception("Type must be an enumeration"); var values = System.Enum.GetValues(t); var dict = new Dictionary<int, string>(); foreach (object obj in values) { string name = System.Enum.GetName(t, obj); dict.Add(Convert.ToInt32(System.Enum.Format(t, obj, "D")), name); } return new JavaScriptSerializer().Serialize(dict); }
با توجه به اینکه در سمت کلاینت مقدار json ذخیره شده در فیلد مخفی را میتوان به صورت آبجکت برخورد کرد پس یک متد در سمت کلاینت این آبجکت را در loop قراد داده و درمتغییری در فایل جاوا اسکریپت نگهداری میکنیم:
var Enum_PersistType = null; function SetEnumTypes() { Enum_PersistType = JSON.parse($('#hfJsonEnum_PersistType').val()); }
و در هر قسمت که نیاز به مقدار enum بود با توجه به ایندکس مقدار را برای نمایش ازاین متغییر بیرون میکشیم:
function GetPersistTypeTitle_Concept(enumId) { return Enum_PersistType[enumId]; }
برای مثال در dropdown در سمت کلاینت این نوع استفاده شده و در حالتی از صفحه فقط برای نمایش عنوان آن احتیاج به دریافت آن از سمت سرور باشد میتوان از این روش کمک گرفت
و یا در سمت javascript میتوان با استفاده از jQuery مقادیر متغییر را در dropdown پر کرد.
function FillDropdown() { $("#ddlPersistType").html(""); $.each(Enum_PersistType, function (key, value) { $("#ddlPersistType").append($("<option></option>").val(key).html(value)); }); }
System.Web.dll System.Web.ApplicationServices.dll
C:\Program Files (x86)\Microsoft ASP.NET\ASP.NET Web Pages\v2.0\Assemblies\WebMatrix.Data.dll C:\Program Files (x86)\Microsoft ASP.NET\ASP.NET Web Pages\v2.0\Assemblies\WebMatrix.WebData.dll
<?xml version="1.0" encoding="utf-8" ?> <configuration> <connectionStrings> <add name="DefaultConnection" connectionString="Data Source=.;Initial Catalog=SimpleMembershipProviderDB;Integrated Security=True;" providerName="System.Data.SqlClient"/> </connectionStrings> <system.web> <roleManager enabled="true" defaultProvider="SimpleRoleProvider"> <providers> <clear/> <add name="SimpleRoleProvider" type="WebMatrix.WebData.SimpleRoleProvider, WebMatrix.WebData"/> </providers> </roleManager> <membership defaultProvider="SimpleMembershipProvider"> <providers> <clear/> <add name="SimpleMembershipProvider" type="WebMatrix.WebData.SimpleMembershipProvider, WebMatrix.WebData"/> </providers> </membership> </system.web> </configuration>
using System; using System.Security.Principal; using System.Web.Security; using WebMatrix.WebData; namespace MemberShipConsoleApplication { class Program { static void Main(string[] args) { WebSecurity.InitializeDatabaseConnection("DefaultConnection", "UserProfile", "UserId", "UserName", autoCreateTables: true); AddUserAndRolSample(); Login(); if (System.Threading.Thread.CurrentPrincipal.Identity.IsAuthenticated) RunApp(); } static void AddUserAndRolSample() { if (WebSecurity.UserExists("iman")) return; // No implements in SimpleMembershipProvider : // Membership.CreateUser("iman", "123"); WebSecurity.CreateUserAndAccount("iman", "123"); Roles.CreateRole("admin"); Roles.CreateRole("User"); Roles.AddUserToRole("iman", "admin"); } static void Login() { for (int i = 0; i < 3; i++) { Console.Write("UserName: "); var userName = Console.ReadLine(); Console.Write("Password: "); var password = Console.ReadLine(); if (Membership.ValidateUser(userName, password)) { var user = Membership.GetUser(userName); var identity = new GenericIdentity(user.UserName); var principal = new RolePrincipal(identity); System.Threading.Thread.CurrentPrincipal = principal; Console.Clear(); return; } Console.WriteLine("User Name Or Password Not Valid."); } } static void RunApp() { Console.WriteLine("Welcome To MemberShip. User Name: {0}", System.Threading.Thread.CurrentPrincipal.Identity.Name); if (System.Threading.Thread.CurrentPrincipal.IsInRole("admin")) Console.WriteLine("Hello Admin User!"); Console.Read(); } } }
InitializeDatabaseConnection(string connectionStringName, string userTableName, string userIdColumn, string userNameColumn, bool autoCreateTables)
نکاتی درباره برنامه نویسی دستوری(امری)
mutable Keyword
در فصل دوم(شناسه ها) گفته شد که برای یک شناسه امکان تغییر مقدار وجود ندارد. اما در #F راهی وجود دارد که در صورت نیاز بتوانیم مقدار یک شناسه را تغییر دهیم.در #F هرگاه بخواهیم شناسه ای تعریف کنیم که بتوان در هر زمان مقدار شناسه رو به دلخواه تغییر داد از کلمه کلیدی mutable کمک میگیریم و برای تغییر مقادیر شناسهها کافیست از علامت (->) استفاده کنیم. به یک مثال در این زمینه دقت کنید:
#1 let mutable phrase = "Can it change? " #2 printfn "%s" phrase #3 phrase <- "yes, it can." #4 printfn "%s" phrase
در خط اول یک شناسه را به صورت mutable(تغییر پذیر) تعریف کردیم و در خط سوم با استفاده از (->) مقدار شناسه رو update کردیم. خروجی مثال بالا به صورت زیر است:
Can it change? yes, it can.
نکته اول: در این روش هنگام update کردن مقدار شناسه حتما باید مقدار جدید از نوع مقدار قبلی باشد در غیر این صورت با خطای کامپایلری متوقف خواهید شد.
#1 let mutable phrase = "Can it change? " #3 phrase <- 1
اجرای کد بالا خطای زیر را به همراه خواهد داشت.(خطا کاملا واضح است و نیاز به توضیح دیده نمیشود)
Prog.fs(9,10): error: FS0001: This expression has type int but is here used with type string
let redefineX() = let x = "One" printfn "Redefining:\r\nx = %s" x if true then let x = "Two" printfn "x = %s" x printfn "x = %s" x
در مثال بالا در تابع redefineX یک شناسه به نام x تعریف کردم با مقدار "One". یک بار مقدار شناسه x رو چاپ میکنیم و بعد دوباره بعد از شرط true یک شناسه دیگر با همون نام یعنی x تعریف شده است و در انتها هم دو دستور چاپ. ابتدا خروجی مثال بالا رو با هم مشاهده میکنیم.
Redefining: x = One x = Two x = One
let mutableX() = let mutable x = "One" printfn "Mutating:\r\nx = %s" x if true then x <- "Two" printfn "x = %s" x printfn "x = %s" x
Mutating: x = One x = Two x = Two
Reference Cells
روشی برای استفاده از شناسهها به صورت mutable است. با این روش میتونید شناسه هایی تعریف کنید که امکان تغییر مقدار برای اونها وجود دارد. زمانی که از این روش برای مقدار دهی به شناسهها استفاده کنیم یک کپی از مقدار مورد نظر به شناسه اختصاص داده میشود نه آدرس مقدار در حافظه.
به جدول زیر توجه کنید:
Member Or Field | Description | Definition |
(derefence operator)! | مقدار مشخص شده را برگشت میدهد | let (!) r = r.contents |
(Assignment operator)=: | مقدار مشخص شده را تغییر میدهد | let (:=) r x = r.contents <- x |
ref operator | یک مقدار را در یک reference cell جدید کپسوله میکند | let ref x = { contents = x } |
Value Property | برای عملیات get یا set مقدار مشخص شده | member x.Value = x.contents |
contents record field | برای عملیات get یا set مقدار مشخص شده | let ref x = { contents = x } |
let refVar = ref 6 refVar := 50
printfn "%d" !refVar
let xRef : int ref = ref 10 printfn "%d" (xRef.Value) printfn "%d" (xRef.contents) xRef.Value <- 11 printfn "%d" (xRef.Value) xRef.contents <- 12 printfn "%d" (xRef.contents)
10 10 11 12
خصیصه اختیاری در #F
در #F زمانی از خصیصه اختیاری استفاده میکنیم که برای یک متغیر مقدار وجود نداشته باشد. option در #F نوعی است که میتواند هم مقدار داشته باشد و هم نداشته باشد.
let keepIfPositive (a : int) = if a > 0 then Some(a) else None
let exists (x : int option) = match x with | Some(x) -> true | None -> false
چگونگی استفاده از option
مثال
let rec tryFindMatch pred list = match list with | head :: tail -> if pred(head) then Some(head) else tryFindMatch pred tail | [] -> None let result1 = tryFindMatch (fun elem -> elem = 100) [ 200; 100; 50; 25 ] //برابر با 100 است let result2 = tryFindMatch (fun elem -> elem = 26) [ 200; 100; 50; 25 ]// برابر با None است
یک مثال کاربردی تر
open System.IO let openFile filename = try let file = File.Open (filename, FileMode.Create) Some(file) with | ex -> eprintf "An exception occurred with message %s" ex.Message None
Enumeration
تقریبا همه با نوع داده شمارشی یا enums آشنایی دارند. در اینجا فقط به نحوه پیاده سازی آن در #F میپردازیم. ساختار کلی تعریف آن به صورت زیر است:
type enum-name = | value1 = integer-literal1 | value2 = integer-literal2 ...
type Color = | Red = 0 | Green = 1 | Blue = 2
let col1 : Color = Color.Red
type uColor = | Red = 0u | Green = 1u | Blue = 2u let col3 = Microsoft.FSharp.Core.LanguagePrimitives.EnumOfValue<uint32, uColor>(2u)
توضیح درباره use
در دات نت خیلی از اشیا هستند که اینترفیس IDisposable رو پیاده سازی کرده اند. این بدین معنی است که حتما یک متد به نام dispose برای این اشیا وجود دارد که فراخوانی آن به طور قطع باعث بازگرداندن حافظه ای که در اختیار این کلاسها بود میشود. برای راحتی کار در #C یک عبارت به نام using وجود دارد که در انتها بلاک متد dispose شی مربوطه را فراخوانی میکند.
using(var writer = new StreamWriter(filePath)) { }
let writeToFile fileName = use sw = new System.IO.StreamWriter(fileName : string) sw.Write("Hello ")
در #F اعداد دارای علامت و اعداد شناور دارای وابستگی با واحدهای اندازه گیری هستند که به نوعی معرف اندازه و حجم و مقدار و ... هستند. در #F شما مجاز به تعریف واحدهای اندازه گیری خاص خود هستید و در این تعاریف نوع عملیات اندازه گیری را مشخص میکنید. مزیت اصلی استفاده از این روش جلوگیری از رخ دادن خطاهای کامپایلر در پروژه است. ساختار کلی تعریف:
[<Measure>] type unit-name [ = measure ]
[<Measure>] type cm
[<Measure>] type ml = cm^3
let value = 1.0<cm>
قدرت اصلی واحدهای اندازه گیری #F در توابع تبدیل است. تعریف توابع تبدیل به صورت زیر میباشد:
[<Measure>] type g تعریف واحد گرم [<Measure>] type kg تعریف واحد کیلوگرم let gramsPerKilogram : float<g kg^-1> = 1000.0<g/kg> تعریف تابع تبدیل
[<Measure>] type degC // دما بر حسب سلسیوس [<Measure>] type degF // دما بر حسب فارنهایت let convertCtoF ( temp : float<degC> ) = 9.0<degF> / 5.0<degC> * temp + 32.0<degF> // تابع تبدیل سلسیوس به فارنهایت let convertFtoC ( temp: float<degF> ) = 5.0<degC> / 9.0<degF> * ( temp - 32.0<degF>) // تابع تبدیل فارنهایت به سلسیوس let degreesFahrenheit temp = temp * 1.0<degF> // درجه به فارنهایت let degreesCelsius temp = temp * 1.0<degC> // درجه به سلسیوس printfn "Enter a temperature in degrees Fahrenheit." let input = System.Console.ReadLine() let mutable floatValue = 0. if System.Double.TryParse(input, &floatValue)// اگر ورودی عدد بود then printfn "That temperature in Celsius is %8.2f degrees C." ((convertFtoC (degreesFahrenheit floatValue))/(1.0<degC>)) else printfn "Error parsing input."
خروجی مثال بالا :
Enter a temperature in degrees Fahrenheit. 90 That temperature in degrees Celsius is 32.22.
ViewEngine.ViewLocationFormats= "~/Views/{controller}/{action}.cshtml"
"~/Themes/{ThemeName}/Views/{controller}/{action}.cshtml"
public static void Themeable(this VirtualPathProviderViewEngine engine) { var ThemePath = "~/Themes"; var ThemeName = WebConfigurationManager.AppSettings["MvcTheme"]; if (string.IsNullOrEmpty(ThemeName)) return; var themeFolder = HttpContext.Current.Server.MapPath(string.Format("{0}/{1}/", ThemePath, ThemeName)); if (!Directory.Exists(themeFolder)) throw new DirectoryNotFoundException(string.Format("Theme folder not exists: {0}/{1}}", ThemePath, ThemeName)); var newViewLocations = new[] { string.Format("{0}/{1}/Views/{2}/{3}.cshtml", ThemePath, ThemeName, "{1}", "{0}"), string.Format("{0}/{1}/Views/Shared/{2}.cshtml", ThemePath, ThemeName, "{0}"), // vb.net : // string.Format("{0}/{1}/Views/{2}/{3}.vbhtml", ThemePath, ThemeName, "{1}", "{0}"), // string.Format("{0}/{1}/Views/Shared/{2}.vbhtml", ThemePath, ThemeName, "{0}"), }; engine.ViewLocationFormats = newViewLocations; engine.PartialViewLocationFormats = newViewLocations; }
<appSettings> ... <add key="MvcTheme" value="Test1" /> </appSettings>
ViewEngines.Engines.OfType<RazorViewEngine>().Single().Themeable();
@{ // Layout = "~/Views/Shared/_Layout.cshtml"; Layout = "~/themes/test1/Views/Shared/_Layout.cshtml"; }
public class ThemeBundle { public BundleType BundleType { get; set; } public string VirtualPath { get; set; } public string[] Urls { get; set; } } public enum BundleType { Style, Script }
public static void RegisterThemeBundels(BundleCollection bundles) { var ThemePath = "~/Themes"; var ThemeName = WebConfigurationManager.AppSettings["MvcTheme"]; var ThemeBundleFileName = "ThemeBundle.json"; List<ThemeBundle> list; try { JavaScriptSerializer jss = new JavaScriptSerializer(); var jsonaddress = System.Web.HttpContext.Current.Server.MapPath(string.Format("{0}/{1}/{2}", ThemePath, ThemeName, ThemeBundleFileName)); var json = System.IO.File.ReadAllText(jsonaddress); list = jss.Deserialize<List<ThemeBundle>>(json); } catch (Exception ex) { throw new Exception(string.Format("Cannot read {0}. see more error in inner exception.", ThemeBundleFileName), ex); } foreach (var themeBundle in list) { switch (themeBundle.BundleType) { case BundleType.Script: bundles.Add(new ScriptBundle(themeBundle.VirtualPath).Include( themeBundle.Urls)); break; case BundleType.Style: bundles.Add(new StyleBundle(themeBundle.VirtualPath).Include( themeBundle.Urls)); break; default: throw new ArgumentOutOfRangeException(nameof(themeBundle.BundleType)); } } }
public class BundleConfig { public static void RegisterBundles(BundleCollection bundles) { if (MvcTheme.ThemeName != null) { MvcTheme.RegisterThemeBundels(bundles); return; } bundles.Add(new ScriptBundle("~/bundles/jquery").Include( "~/Scripts/jquery-{version}.js")); bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include( "~/Scripts/jquery.validate*")); ... } }
[ { "BundleType": "Script", "VirtualPath": "~/themes/test1/js/jquery", "Urls": [ "~/themes/test1/js/jquery-1.10.2.js" ] }, { "BundleType": "Script", "VirtualPath": "~/themes/test1/js/jqueryval", "Urls": [ "~/themes/test1/js/jquery.validate.js", "~/themes/test1/js/jquery.validate.unobtrusive.js" ] }, { "BundleType": "Script", "VirtualPath": "~/themes/test1/js/modernizr", "Urls": [ "~/themes/test1/js/modernizr-2.6.2.js" ] }, { "BundleType": "Script", "VirtualPath": "~/themes/test1/js/bootstrap", "Urls": [ "~/themes/test1/js/bootstrap.js", "~/themes/test1/js/respond.js" ] }, { "BundleType": "Style", "VirtualPath": "~/themes/test1/css/css", "Urls": [ "~/themes/test1/css/bootstrap.css", "~/themes/test1/css/site.css" ] } ]
Themes ├───Test1 │ │ThemeBundle.json │ ├───Css │ ├───Fonts │ ├───Images │ ├───Js │ └───Views ├───Test2 │ │ThemeBundle.json │ ├───Css │ ├───Fonts │ ├───Images │ ├───Js │ └───Views
public static class MvcTheme { public static string ThemeName { get; } public static string ThemePath { get; set; } private const string AppSettingName = "MvcTheme"; private const string ThemeBundleFileName = "ThemeBundle.json"; static MvcTheme() { ThemePath = "~/Themes"; ThemeName = WebConfigurationManager.AppSettings[AppSettingName]; } public static void Themeable(this VirtualPathProviderViewEngine engine) { if (string.IsNullOrEmpty(ThemeName)) return; var themeFolder = HttpContext.Current.Server.MapPath(string.Format("{0}/{1}/", ThemePath, ThemeName)); if (!Directory.Exists(themeFolder)) throw new DirectoryNotFoundException(string.Format("Theme folder not exists: {0}/{1}}", ThemePath, ThemeName)); var newViewLocations = new[] { string.Format("{0}/{1}/Views/{2}/{3}.cshtml", ThemePath, ThemeName, "{1}", "{0}"), string.Format("{0}/{1}/Views/Shared/{2}.cshtml", ThemePath, ThemeName, "{0}"), // vb.net : // string.Format("{0}/{1}/Views/{2}/{3}.vbhtml", ThemePath, ThemeName, "{1}", "{0}"), // string.Format("{0}/{1}/Views/Shared/{2}.vbhtml", ThemePath, ThemeName, "{0}"), }; engine.ViewLocationFormats = newViewLocations; engine.PartialViewLocationFormats = newViewLocations; } public static void RegisterThemeBundels(BundleCollection bundles) { if(ThemeName == null) return; var list = ReadThemeBundles(); foreach (var themeBundle in list) { switch (themeBundle.BundleType) { case BundleType.Script: bundles.Add(new ScriptBundle(themeBundle.VirtualPath).Include( themeBundle.Urls)); break; case BundleType.Style: bundles.Add(new StyleBundle(themeBundle.VirtualPath).Include( themeBundle.Urls)); break; default: throw new ArgumentOutOfRangeException(nameof(themeBundle.BundleType)); } } } public static List<ThemeBundle> ReadThemeBundles() { try { JavaScriptSerializer jss = new JavaScriptSerializer(); var jsonaddress = System.Web.HttpContext.Current.Server.MapPath(string.Format("{0}/{1}/{2}", ThemePath, ThemeName, ThemeBundleFileName)); var json = System.IO.File.ReadAllText(jsonaddress); var list = jss.Deserialize<List<ThemeBundle>>(json); return list; } catch (Exception ex) { throw new Exception(string.Format("Cannot read {0}. see more error in inner exception.", ThemeBundleFileName), ex); } } } public class ThemeBundle { public BundleType BundleType { get; set; } public string VirtualPath { get; set; } public string[] Urls { get; set; } } public enum BundleType { Style, Script }