نسخه Html آنلاین: http://www.tutorialspoint.com/developers_best_practices/index.htm
Local Storage
با استفاده از Local Storage قادر خواهیم بود تا دادههایی را در سمت کلاینت ذخیره کنیم؛ بدون اینکه بر عملکرد سایت تاثیر بگذارد. منظور از تاثیر بر روی عملکرد این است که در هر رفت به سمت سرور لازم نیست با درخواست ارسالی، دادههای اضافهای ارسال شوند.
با استفاده از Local Storage قادر خواهیم بود حداقل 5 مگابایت داده را ذخیره کنیم.
استفاده از Local Storage با استفاده از دو شیء زیر امکان پذیر میباشد:
- window.localStorage : اطلاعات ذخیره شده در آن فاقد تاریخ انقضاء
میباشند و تا زمانی که اقدام به حذف آن ننمایید، دادههای آن معتبر
میباشند.
- window.sessionStorage : اطلاعات ذخیره شده در آن تا زمان بسته شدن تب مرورگر، معتبر میباشند. با بسته شدن تب فعلی مرورگر، تمامی اطلاعات ذخیره شده از بین خواهند رفت.
نحوه استفاده:
- نحوه افزودن داده در Local Storage به صورت کلید/ مقدار میباشد.
ابتدا نگاهی داشته باشیم به اینترفیس Storage:
interface Storage { readonly attribute unsigned long length; DOMString? key(unsigned long index); getter DOMString? getItem(DOMString key); setter void setItem(DOMString key, DOMString value); deleter void removeItem(DOMString key); void clear(); }
if (typeof(Storage) !== "undefined") { // do ... }
شیء window.localStorage
همانطور که بیان کردیم، در این روش داده دارای تاریخ انقضاء نمیباشد.
() SetItem :
متد setItem برای ذخیره اطلاعات است و نحوه استفاده از آن به صورت زیر میباشد:
localStorage.setItem("lastpost", "localstorage");
localStorage.setItem("visitorCount",15 ); localStorage.setItem("visitorCount", 16);
() getItem :
برای دسترسی به مقدار ذخیره شده میتوانیم از متد getItem به همراه مقدار کلید استفاده کنیم. در صورت موجود نبودن مقدار، null برگردانده خواهد شد.
localStorage.getItem("lastpost");
localStorage.lastpost = "localstorage"; document.getElementById("result").innerHTML = localStorage.lastpost;
و برای حذف اطلاعات ذخیره شده:
localStorage.removeItem("lastpost");
خصوصیت length :
تعداد جفت کلید / مقدارهای جاری را بر میگرداند.
متد Key :
این متد مقداری را از ورودی دریافت کرده و اسم کلید مورد نظر را بر میگرداند. ایندکس آن از صفر شروع خواهد شد. قطعه کد زیر باعث برگرداندن مقدار lastname میشود:
localStorage.setItem("name","uthman" ); localStorage.setItem("lastname","24" ); alert(localStorage.key(1));
این متد باعث پاک شدن تمامی دادههای ذخیره شده خواهد شد.
شی sessionStorage :
از نظر syntax دستوری همانند localStorage میباشد و تفاوت آن فقط در زمان ذخیره سازی است؛ با بسته شدن تب مرورگر، تمامی دادههای ذخیره شده پاک خواهند شد.
کتابخانههای مرتبط :
Locker :
با استفاده از این کتابخانه ، قادر خواهید بود تا اطلاعات را با فرمتهای مورد نظر، بدون convert آنها ذخیره نمایید. نمونهای از آن به صورت زیر خواهد بود:
Lockr.set('website', 'SitePoint'); // string Lockr.set('categories', 8); // number Lockr.set('users', [{ name: 'John Doe', age: 18 }, { name: 'Jane Doe', age: 19 }]);
secStore.js :
با استفاده از کتابخانه SJCL امنیت دادههای ذخیره شده، بالا میروند.
var storage = new secStore , options = { encrypt: true, data: { key: 'some data that is somewhat private' } }; storage.set(options, function(err, results){ if (err) throw err; console.log(results); });
و سایر کتابخانههای مرتبط :
یک برنامهی Vue.js را ایجاد کنید و سپس یک پوشه را در فولدر src بنام mixins بسازید. در این پوشه یک فایل را با نام دلخواهی از نوع جاوااسکریپت، ایجاد کنید و محتوای زیر را در آن قرار دهید:
export default { data() { return { title: 'Mixins are cool', copyright: 'All rights reserved. Product of super awesome people' }; }, created: function () { this.greetings(); }, methods: { greetings() { console.log('Howdy my good fellow!'); } } };
برای استفاده از mixin بشکل زیر عمل میکنیم. در واقع کد زیر شامل تمام موارد تعریف شده در myMixin.js میباشد.
<script> import myMixin from './mixins/myMixin' export default { name: 'app', //را دریافت میکند mixins آرایه ای از mixins:[myMixin] } </script> <style>
نکته: در صورتیکه بین mixin و کامپوننت، دادههای همنامی وجود داشته باشد، اولویت با داده یا تابعی است که در خود کامپوننت تعریف شدهاست. مثال زیر را در نظر بگیرید:
export default { data() { return { blogName: 'google.com' }; }, methods: { print() { console.log(this.blogName); } } };
و در کامپوننتی که از mixin فوق استفاده میکند:
<script> import myMixin from "./mixins/myMixin"; import duplicateFuncData from "./mixins/duplicateFuncData"; export default { name: "app", data() { return { // و کامپوننت جاری تکراری ست mixin نام این متغیر در blogName: "microsoft.com" }; }, methods: { // و کامپوننت جاری تکراری ست ولی عملکرد متفاوت دارد mixin نام این تابع در print() { alert(this.blogName); } }, components: {}, //را دریافت میکند mixins آرایه ای از mixins: [myMixin, duplicateFuncData] }; </script>
و نتیجهی اجرا:
تعریف mixin بصورت سراسری: وقتی یک mixin را بصورت global تعریف میکنیم، تمام نمونههای وهله سازی شده از vue، دارای قابلیتهای تعریف شدهی در mixin میباشند. کد main.js را بشکل زیر تغییر میدهیم. اکنون با اجرای برنامه، به ازای هر نمونهای از vue که وهله سازی میشود، تابع زیر اجرا میگردد.
import Vue from 'vue' import App from './App.vue' Vue.config.productionTip = false // بصورت سراسری تعریف شده Vue.mixin({ created: function () { alert('from global mixin') } }) new Vue({ render: h => h(App), }).$mount('#app')
نتیجهگیری:
1) استفاده از mixin باعث اجتناب از تکرار کدها و دادههای تکراری میشود (DRY).
2) از mixin در ساخت پلاگین برای Vue.js استفاده میشود.
// 3. inject some component options Vue.mixin({ created: function () { // some logic ... } ... })
3) اگر mixin و کامپوننتی که از mixin استفاده میکند، هر دو توابع lifecycle را پیاده سازی کرده باشند، اول توابع lifecycle مربوط به mixin اجرا میشود و سپس توابع lifecycle مربوط به کامپوننت.
4) اگر دو کامپوننت، mixin مشترکی را استفاده کنند، دادههای آنها share نخواهد شد و برای اینکه دو کامپوننتی که از mixin واحدی استفاده میکنند بتوانند از دادههای یکسانی در mixin استفاده کنند، نیاز به تزریق وابستگی دارند.
5) اگر از ادغام قسمت جاوااسکریپتی و HTML مربوط به کامپوننتها ناراضی هستید، یک راه حل جداسازی، استفاده از mixin به ازای هر کامپوننت است.
6) استفاده از mixin باعث به روزسانی، نگهداری و توسعهی سادهتر و همچنین ماژولار بودن برنامه میشود.
نکته: برای اجرای برنامه و دریافت پکیجهای مورد استفاده در مثال جاری، نیاز است دستور زیر را اجرا کنید:
npm install
internal interface IValidation { bool IsValid(string sendNamesEndTo, string handlerNamesEndTo); }
public bool IsValid(string commandNamesEndTo = "Command", string commandHandlersEndTo = "CommandHandler") { var assemblies = AppDomain.CurrentDomain.GetAssemblies(); var commandTypeInfos = assemblies.SelectMany(x => x.DefinedTypes.Where(typeInfo => typeInfo.Name.ToLower().EndsWith(commandNamesEndTo.ToLower()) && typeInfo.ImplementedInterfaces.Any(type => type == typeof(IBaseRequest)))); var memberInfos = commandTypeInfos as TypeInfo[] ?? commandTypeInfos.ToArray(); if (!memberInfos.Any()) throw new ArgumentException("Can not find any Command"); var handlerTypeInfo = assemblies.SelectMany(x => x.DefinedTypes.Where(typeInfo => typeInfo.Name.ToLower().EndsWith(commandHandlersEndTo.ToLower()))); var typeInfos = handlerTypeInfo as TypeInfo[] ?? handlerTypeInfo.ToArray(); if (!typeInfos.Any()) throw new ArgumentException("Can not find any CommandHandler"); if (typeInfos.Count() != memberInfos.Count()) return false; return !(from typeInfo in memberInfos let interfaces = typeInfos.SelectMany(x => x.ImplementedInterfaces) where interfaces.Any(x => x.GenericTypeArguments.All(type => type != typeInfo)) select typeInfo).Any(); }
public bool IsValid(string commandNamesEndTo = "Command", string commandHandlersEndTo = "CommandHandler")
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
var commandTypeInfos = assemblies.SelectMany(x => x.DefinedTypes.Where(typeInfo => typeInfo.Name.ToLower().EndsWith(commandNamesEndTo.ToLower()) && typeInfo.ImplementedInterfaces.Any(type => type == typeof(IBaseRequest)))); var memberInfos = commandTypeInfos as TypeInfo[] ?? commandTypeInfos.ToArray(); if (!memberInfos.Any()) throw new ArgumentException("Can not find any Command");
var handlerTypeInfo = assemblies.SelectMany(x => x.DefinedTypes.Where(typeInfo => typeInfo.Name.ToLower().EndsWith(commandHandlersEndTo.ToLower()))); var typeInfos = handlerTypeInfo as TypeInfo[] ?? handlerTypeInfo.ToArray(); if (!typeInfos.Any()) throw new ArgumentException("Can not find any CommandHandler");
if (typeInfos.Count() != memberInfos.Count()) return false;
return !(from typeInfo in memberInfos let interfaces = typeInfos.SelectMany(x => x.ImplementedInterfaces) where interfaces.Any(x => x.GenericTypeArguments.All(type => type != typeInfo)) select typeInfo).Any();
var validCommandConfiguration = new CommandValidator().IsValid();
Git
EF Code First #4
آیا امکان این وجود داره که Connection string و تنظیمات مربوط به اون مثل نام کاربری دیتابیس و رمز عبور و نام دیتابیس ،رو موقع نصب اولین بار برنامه از کاربر دریافت کنیم؟ مثل دات نت نیوک،که همه عملیات به صورت داینامیک انجام بشه؟
و اینکه این عملیات فقط یک بار در هنگام اولین نصب برنامه انجام بگیره و در ادامه دیگه این کار انجام نشه؟
اگر بله! مکان قرار دادن کد ایجاد دیتابیس از روی مدل رو کجای برنامه قرار بدیم بهتره؟ مثلا تو پروژه MVC
public class User { public string UserName { get; set; } public bool IsRole { get; set; } public HashSet<string> ConnectionIds { get; set; } } [Authorize] [HubName("userActivityHub")] public class UserActivityHub : Hub { private static readonly ConcurrentDictionary<string, User> Users = new ConcurrentDictionary<string, User>(); public void AdminJoin() { Groups.Add(Context.ConnectionId, "admins"); } public void Join() { var userName = Context.User.Identity.Name; var connectionId = Context.ConnectionId; var isAdmin = Context.User.IsInRole("Admin"); var user = Users.GetOrAdd(userName, _ => new User { UserName = userName, IsRole = isAdmin, ConnectionIds = new HashSet<string>() }); if (user.IsRole == true) { Groups.Add(user.ConnectionIds.ToString(), "admins"); } else { lock (user.ConnectionIds) { user.ConnectionIds.Add(connectionId); } Clients.Group("admins").showUserCount(Users.Count(a => a.Value.IsRole != true)); } } public void GetUserCount() { Clients.Group("admins").showUserCount(Users.Count(a => a.Value.IsRole != true)); } public override System.Threading.Tasks.Task OnDisconnected(bool stopCalled) { if (stopCalled) { var userName = Context.User.Identity.Name; var connectionId = Context.ConnectionId; User user; Users.TryGetValue(userName, out user); if (user != null) { lock (user.ConnectionIds) { user.ConnectionIds.RemoveWhere(cid => cid.Equals(connectionId)); if (!user.ConnectionIds.Any()) { User removeUser; Users.TryRemove(userName, out removeUser); } } } return Clients.Group("admins").showUserCount(Users.Count(a => a.Value.IsRole != true)); } else { return base.OnDisconnected(false); } } }
<script type="text/javascript"> var userHub = $.connection.userActivityHub; $.connection.hub.logging = true; $.connection.hub.start().done(function() { userHub.server.join(); }); $(function() { window.onbeforeunload = function() { $.connection.hub.stop(); }; }); </script>
<script type="text/javascript"> var userHub = $.connection.userActivityHub; userHub.client.showUserCount = function (message) { $('#userOnlineCount').html(message); }; $.connection.hub.start().done(function() { userHub.server.adminJoin().done(function() { userHub.server.getUserCount(); }); }); </script>
<script> $('a.btn.btn-danger.btn-block').click(function(e) { e.preventDefault(); $('#logoutForm').submit(); $.connection.userActivityHub.connection.stop(); }); $(function() { window.onbeforeunload = function(e) { $.connection.hub.stop(); }; }); </script>
Postable
ارسال سورس کد برنامهها در بلاگر داستان خودش را دارد که پیشتر در مورد آن بحث شد.
اما اینکار (تبدیل کاراکترهای غیرمجاز به نمونههای مجاز یا به اصطلاح escape آنها) پس از یک مدت تبدیل به دردسر خواهد شد. به همین جهت برنامهی کوچک زیر را برای سادهتر کردن این وضع تهیه کردهام، که از آدرس زیر قابل دریافت است:
دریافت برنامه (برای اجرا نیاز به دات نت فریم ورک 2 دارد)
این برنامهی کمکی، انجام چند کار زیر را در بلاگر برای شما سادهتر خواهد کرد:
الف) escape خودکار کاراکترهای غیرمجاز xml هنگام ارسال سورس کدهای خود و همچنین قرار دادن آنها داخل تگهای div و pre مناسب.
روش برنامه نویسی آن:
public static string EscapeXml(string s)
{
var xml = s;
if (!string.IsNullOrEmpty(xml))
{
// replace literal values with entities
xml = xml.Replace("&", "&");
xml = xml.Replace("<", "<");
xml = xml.Replace(">", ">");
xml = xml.Replace("\"", """);
xml = xml.Replace("'", "'");
}
return xml;
}
<table>
<tr>
<td>data
</td>
</tr>
<table> <tr> <td>data</td> </tr>
روش برنامه نویسی آن :
private static readonly Regex REGEX_BETWEEN_TAGS = new Regex(@">\s+<", RegexOptions.Compiled);
private static readonly Regex REGEX_LINE_BREAKS = new Regex(@"\n\s+", RegexOptions.Compiled);
public static string RemoveSpaces(string html)
{
html = REGEX_BETWEEN_TAGS.Replace(html, "> <");
return REGEX_LINE_BREAKS.Replace(html, string.Empty);
}
ج) حذف کاراکتر 0xA0 . البته این مورد ارتباطی به بلاگر پیدا نمیکند ولی اگر با CPP کار کرده باشید، حتما به مورد کپی سورس از اینترنت به داخل ادیتور و عدم کامپایل آن، برخوردهاید. در سورس کدهای CPP مجاز به استفاده از کاراکتر No-Break Space نیستید (0xA0) و باید حذف شود. حال فرض کنید با بیش از 200 سطر سر و کار دارید. بنابراین نیاز به یک تمیز کننده سریع وجود خواهد داشت. (این مورد در ادیتور برنامه management studio اس کیوال سرور هم صادق است)
txtMod.Text = txtOrig.Text.Replace((char)160, ' ');
نگاشت APM به یک Task
در قسمت اول، نمونه مثالی را از APM، که در آن کار با BeginGetResponse آغاز شده و سپس در callback نهایی توسط EndGetResponse، نتیجهی عملیات به دست میآید، مشاهده کردید. در ادامه میخواهیم یک محصور کنندهی جدید را برای این نوع API قدیمی تهیه کنیم، تا آنرا به صورت یک Task ارائه دهد.
public static class ApmWrapper { public static Task<int> ReadAsync(this Stream stream, byte[] data, int offset, int count) { return Task<int>.Factory.FromAsync(stream.BeginRead, stream.EndRead, data, offset, count, null); } }
در مثال فوق BeginRead و EndRead استفاده شده از نوع delegate هستند. چون خروجی EndRead از نوع int است، خروجی متد نیز از نوع Task of int تعیین شدهاست. همچنین سه پارامتر ابتدایی BeginRead ، دقیقا data، offset و count هستند. دو پارامتر آخر آن callback و state نام دارند. پارامتر callback توسط متد FromAsync فراهم میشود و state نیز در اینجا null درنظر گرفته شدهاست.
یک مثال استفاده از آنرا در ادامه مشاهده میکنید:
using System; using System.IO; using System.Threading.Tasks; namespace Async06 { public static class ApmWrapper { public static Task<int> ReadAsync(this Stream stream, byte[] data, int offset, int count) { return Task<int>.Factory.FromAsync(stream.BeginRead, stream.EndRead, data, offset, count, null); } } class Program { static void Main(string[] args) { using (var stream = File.OpenRead(@"..\..\program.cs")) { var data = new byte[10000]; var task = stream.ReadAsync(data, 0, data.Length); Console.WriteLine("Read bytes: {0}", task.Result); } } } }
البته همانطور که پیشتر نیز عنوان شد، استفاده از خاصیت Result، اجرای کد را بجای غیرهمزمان بودن، به حالت همزمان تبدیل میکند.
در اینجا چون خروجی متد ReadAsync یک Task است، میتوان از متد ContinueWith نیز بر روی آن جهت دریافت نتیجه استفاده کرد:
using (var stream = File.OpenRead(@"..\..\program.cs")) { var data = new byte[10000]; var task = stream.ReadAsync(data, 0, data.Length); task.ContinueWith(t => Console.WriteLine("Read bytes: {0}", t.Result)).Wait(); }
یک نکته
پروژهی سورس بازی به نام Async Generator در GitHub، سعی کردهاست برای ساده سازی نوشتن محصور کنندههای مبتنی بر Task روش APM، یک Code generator تولید کند. فایلهای آنرا از آدرس ذیل میتوانید دریافت کنید:
نگاشت EAP به یک Task
نمونهای از Event based asynchronous pattern یا EAP را در قسمت اول، زمانیکه روال رخدادگردان webClient.DownloadStringCompleted را بررسی کردیم، مشاهده نمودید. کار کردن با آن نسبت به APM بسیار سادهتر است و نتیجهی نهایی عملیات غیرهمزمان را در یک روال رخدادگران، در اختیار استفاده کننده قرار میدهد. همچنین در روش EAP، اطلاعات در همان Synchronization Context ایی که عملیات شروع شدهاست، بازگشت داده میشود. به این ترتیب اگر آغاز کار در ترد UI باشد، نتیجه نیز در همان ترد دریافت خواهد شد. به این ترتیب دیگر نگران دسترسی به مقدار آن در کارهای UI نخواهیم بود؛ اما در APM چنین ضمانتی وجود ندارد.
متاسفانه TPL همانند روش FromAsync معرفی شده در ابتدای بحث، راه حل توکاری را برای محصور سازی متدهای روش EAP ارائه ندادهاست. اما با استفاده از امکانات TaskCompletionSource آن میتوان چنین کاری را انجام داد. در ادامه سعی خواهیم کرد همان متد الحاقی توکار DownloadStringTaskAsync ارائه شده در دات نت 4.5 را از صفر بازنویسی کنیم.
public static class WebClientExtensions { public static Task<string> DownloadTextTaskAsync(this WebClient web, string url) { var tcs = new TaskCompletionSource<string>(); DownloadStringCompletedEventHandler handler = null; handler = (sender, args) => { web.DownloadStringCompleted -= handler; if (args.Cancelled) { tcs.SetCanceled(); } else if(args.Error!=null) { tcs.SetException(args.Error); } else { tcs.SetResult(args.Result); } }; web.DownloadStringCompleted += handler; web.DownloadStringAsync(new Uri(url)); return tcs.Task; } }
سپس از TaskCompletionSource برای تبدیل این عملیات به یک Task کمک میگیریم. اگر args.Cancelled مساوی true باشد، یعنی عملیات دریافت فایل لغو شدهاست. بنابراین متد SetCanceled منبع Task ایجاد شده را فراخوانی خواهیم کرد. این مورد استثنایی را در کدهای فراخوان سبب میشود. به همین دلیل بررسی خطا با یک if else پس از آن انجام شدهاست. برای بازگشت خطای دریافت شده از متد SetException و برای بازگشت نتیجهی واقعی دریافتی، از متد SetResult میتوان استفاده کرد.
به این ترتیب متد الحاقی غیرهمزمان جدیدی را به نام DownloadTextTaskAsync برای محصور سازی متد EAP ایی به نام DownloadStringAsync و همچنین رخدادگران آن تهیه کردیم.
- ۷ افزونه مختلف فایرفاکس برای بررسی سورس سایت ها | مصطفی لامعی | iclub.ir
- آموزش نوشتن Parser با VeParser | www.persiadevelopers.com
- تغییر رفتار Session از طریق کدنویسی در ASP.NET4.0 | mojtabasahraei | mojtabasahraei.blogfa.com
- ثابت ماندن اسکرول تری ویو در هنگام کار با Ajax | mojtabasahraei | mojtabasahraei.blogfa.com
- درد دل | عارف حکیمی | www.parnianportal.com
- نمایه استنادی کتابها: محصولی جدید از ISI | سیدمهدی حسینی | www.semaho.ir
- همسان کردن اطلاعات تیم. | masoud.daneshpour.org
- ASP.Net Custom Server Control Property Collection | masoud.daneshpour.org
- 98 درصد درآمد موزیلا از طریق پورسانت سایتهای جستجو تامین میشود | www.favbrowser.com
- بررسی Mono 2.11 | tirania.org