یادگیری مرحله به مرحله Microsoft BI
<html> <body> <div> Prevent default: <input type="checkbox" id="keydownStop" value="1" /> keydown <input type="checkbox" id="keypressStop" value="1" /> keypress <input type="checkbox" id="keyupStop" value="1" /> keyup </div> Ignore: <input type="checkbox" id="keydownIgnore" value="1" /> keydown <input type="checkbox" id="keypressIgnore" value="1" /> keypress <input type="checkbox" id="keyupIgnore" value="1" /> keyup <div> Focus on the input below and press any key. </div> <div> <input type="text" style=" width: 600px" id="keyInput" /> </div> Log: <div> <textarea id="keyLogger" rows="18" onfocus="this.blur()" style="width: 600px; border: 1px solid black"></textarea> </div> <input type="button" value="Clear" onclick="clearLog()" /> <script type="text/javascript"> document.getElementById('keyInput').onkeydown = keyHandler; document.getElementById('keyInput').onkeyup = keyHandler; document.getElementById('keyInput').onkeypress = keyHandler; document.getElementById('keyInput').focus(); function keyHandler(e) { e = e || window.event; if (document.getElementById(e.type + 'Ignore').checked) return; var evt = e.type; while (evt.length < 10) evt += ' ' + log(evt + ' keyCode=' + e.keyCode + ' which=' + e.which + ' charCode=' + e.charCode + ' char=' + String.fromCharCode(e.keyCode || e.charCode) + (e.shiftKey ? ' +shift' : '') + (e.ctrlKey ? ' +ctrl' : '') + (e.altKey ? ' +alt' : '') + (e.metaKey ? ' +meta' : '')); if (document.getElementById(e.type + 'Stop').checked) { e.preventDefault ? e.preventDefault() : (e.returnValue = false); } } function clearLog() { document.getElementById('keyLogger').value = ''; document.getElementById('keyInput').focus(); } function log(text) { var area = document.getElementById('keyLogger'); area.value += text + '\n'; area.scrollTop = area.scrollHeight; } </script> </body> </html>
<!doctype html>
document.onkeydown = function (e) { e = e || event; console.log(e); }
function getCharacter(event) { if (event.which == null) return String.fromCharCode(event.keyCode); // IE else if (event.which != 0 && event.charCode != 0) return String.fromCharCode(event.which); // All others return null; // special key }
<html> <body> <input id="inputText" type="text" /> <script> document.onkeydown = keyDown; document.onkeypress = keyPress; document.onkeyup = keyUp; function keyDown(e) { var input = document.getElementById('inputText'); input.value = 'keyDown started ...'; input.disabled = true; var j = 0; for (var i = 0; i < 999999999; i++) { j = i - j; } console.log(j); //alert('keyDown'); input.value = 'keyDown finished.'; input.disabled = false; } function keyPress(e) { alert('keyPress'); //console.log('keyPress'); } function keyUp(e) { alert('keyUp'); //console.log('keyUp'); } </script> </body> </html>
<html> <head> <script type="text/javascript"> function process() { var above = 0, below = 0; for (var i = 0; i < 200000; i++) { if (Math.random() * 2 > 1) { above++; } else { below++; } } } function test() { var result1 = document.getElementById('log'); var start = new Date().getTime(); console.log('start'); for (var i = 0; i < 200; i++) { result1.value = 'time=' + (new Date().getTime() - start) + ' [i=' + i + ']'; process(); } result1.value = 'time=' + (new Date().getTime() - start) + ' [done]'; console.log('end'); } window.onload = test; </script> </head> <body> <input id='log' /> </body> </html>
<input onkeydown="return false" /> <input onkeypress="return false" />
document.getElementById('myInputText').onkeypress = function (e) { var char = getCharacter(e || window.event); if (!char) return; // special key this.value += char.toUpperCase(); return false; // برای اینکه کاراکتر اضافی نمایش داده نشود }
e ? e : window.event;
document.getElementById('numberInputText').onkeypress = function (e) { e = e || window.event; var chr = getCharacter(e); if (!isNumeric(chr) && chr !== null) return false; } function isNumeric(n) { return !isNaN(parseFloat(n)) && isFinite(n); } function isNumber(val) { return val !== "NaN" && (+val) + '' === val + '' }
0==false // true 0===false // false, because they are of a different type 1=="1" // true, auto type coercion 1==="1" // false, because they are of a different type
e.ctrlKey && e.keyCode == 'A'.charCodeAt(0)
<html> <body> <div id="dotnettips" style="width: 35px; height: 35px; background-image: url(https://www.dntips.ir/favicon.ico); position: absolute; left: 10px; top: 10px;" tabindex="0"> </div> <script> document.getElementById('dotnettips').onkeydown = function (e) { e = e || event; switch (e.keyCode) { case 37: // left this.style.left = parseInt(this.style.left) - this.offsetWidth + 'px'; return false; case 38: // up this.style.top = parseInt(this.style.top) - this.offsetHeight + 'px'; return false; case 39: // right this.style.left = parseInt(this.style.left) + this.offsetWidth + 'px'; return false; case 40: // down this.style.top = parseInt(this.style.top) + this.offsetHeight + 'px'; return false; } } </script> </body> </html>
<html> <body> <input id="inputText" type="text" /> <div id="divCapsLock" style="color: Red; display: none;">Caps Lock is ON!</div> <script> var capsLock = null; document.onkeypress = keyPress; document.onkeydown = keyDown; function keyDown(e) { e = e || event; capsLock = (e.keyCode == 20 && capsLock !== null) ? !capsLock : capsLock; document.getElementById('divCapsLock').style.display = capsLock ? 'block' : 'none'; } function keyPress(e) { if (capsLock != null) return; e = e || window.event; var charCode = e.charCode || e.keyCode; capsLock = (charCode >= 97 && charCode <= 122 && e.shiftKey) || (charCode >= 65 && charCode <= 90 && !e.shiftKey); document.getElementById('divCapsLock').style.display = capsLock ? 'block' : 'none'; } </script> </body> </html>
<html> <body> <div id='divInput'> <input id="inputText" type="text" /> </div> <script> document.getElementById('inputText').onkeydown = inputKeyDown; document.getElementById('divInput').onkeydown = divKeyDown; document.onkeydown = documentKeyDown; window.onkeydown = windowKeyDown; function divKeyDown(e) { console.log('divKeyDown'); } function inputKeyDown(e) { console.log('inputKeyDown'); } function documentKeyDown(e) { console.log('documentKeyDown'); } function windowKeyDown(e) { console.log('windowKeyDown'); } </script> </body> </html>
<html> <body> <input id="inputText" type="text" /> <div id="divCapsLock" style="color: Red; display: none;">Caps Lock is ON!</div> <script> var capsLock = null; var hasFocus = false; document.onkeyup = keyUp; document.onkeypress = keyPress; document.getElementById('inputText').onfocus = focus; document.getElementById('inputText').onblur = focus; function warnCapsLock() { document.getElementById('divCapsLock').style.display = (capsLock != null && capsLock && hasFocus) ? 'block' : 'none'; } function focus() { hasFocus = !hasFocus; warnCapsLock(); } function keyUp(e) { e = e || event; capsLock = (e.keyCode == 20 && capsLock !== null) ? !capsLock : capsLock; warnCapsLock(); } function keyPress(e) { if (capsLock != null) return; e = e || window.event; var charCode = e.charCode || e.keyCode; capsLock = (charCode >= 97 && charCode <= 122 && e.shiftKey) || (charCode >= 65 && charCode <= 90 && !e.shiftKey); warnCapsLock(); } </script> </body> </html>
window.onblur = function () { capsLock = null; }
var Client = {}; Client.Keyboard = {}; Client.Keyboard.EnableKeyDown = true; Client.Keyboard.EnableKeyUp = false; Client.Keyboard.CurrentKeyEvent = null; window.onkeydown = function (event) { if (!Client.Keyboard.EnableKeyDown) return true; return KeyboardEvents(event); }; window.onkeyup = function (event) { if (!Client.Keyboard.EnableKeyUp) return true; return KeyboardEvents(event); }; function Rise(event) { var e = Client.Keyboard.CurrentKeyEvent; if (event) { var data = { shift: e.shiftKey, ctrl: e.ctrlKey, alt: e.altKey }; if (!event(data)) return false; return event(data); } return true; } function KeyboardEvents(e) { e = e || window.event; Client.Keyboard.CurrentKeyEvent = e; switch (e.keyCode) { case 8: return Rise(Client.Keyboard.Backspace); case 9: return Rise(Client.Keyboard.Tab); case 13: return Rise(Client.Keyboard.Enter); case 16: return Rise(Client.Keyboard.Shift); case 17: return Rise(Client.Keyboard.Ctrl); case 18: return Rise(Client.Keyboard.Alt); case 19: return Rise(Client.Keyboard.Pause); case 20: return Rise(Client.Keyboard.CapsLock); case 27: return Rise(Client.Keyboard.Esc); case 33: return Rise(Client.Keyboard.PageUp); case 34: return Rise(Client.Keyboard.PageDown); case 35: return Rise(Client.Keyboard.End); case 36: return Rise(Client.Keyboard.Home); case 37: return Rise(Client.Keyboard.Left); case 38: return Rise(Client.Keyboard.Up); case 39: return Rise(Client.Keyboard.Right); case 40: return Rise(Client.Keyboard.Down); case 44: return Rise(Client.Keyboard.PrtScr); case 45: return Rise(Client.Keyboard.Insert); case 46: return Rise(Client.Keyboard.Delete); ////////////////////////////////////////////////////////////////////////////////////////////////// case 48: return Rise(Client.Keyboard.Num0); case 49: return Rise(Client.Keyboard.Num1); case 50: return Rise(Client.Keyboard.Num2); case 51: return Rise(Client.Keyboard.Num3); case 52: return Rise(Client.Keyboard.Num4); case 53: return Rise(Client.Keyboard.Num5); case 54: return Rise(Client.Keyboard.Num6); case 55: return Rise(Client.Keyboard.Num7); case 56: return Rise(Client.Keyboard.Num8); case 57: return Rise(Client.Keyboard.Num9); ////////////////////////////////////////////////////////////////////////////////////////////////// case 65: return Rise(Client.Keyboard.A); case 66: return Rise(Client.Keyboard.B); case 67: return Rise(Client.Keyboard.C); case 68: return Rise(Client.Keyboard.D); case 69: return Rise(Client.Keyboard.E); case 70: return Rise(Client.Keyboard.F); case 71: return Rise(Client.Keyboard.G); case 72: return Rise(Client.Keyboard.H); case 73: return Rise(Client.Keyboard.I); case 74: return Rise(Client.Keyboard.J); case 75: return Rise(Client.Keyboard.K); case 76: return Rise(Client.Keyboard.L); case 77: return Rise(Client.Keyboard.M); case 78: return Rise(Client.Keyboard.N); case 79: return Rise(Client.Keyboard.O); case 80: return Rise(Client.Keyboard.P); case 81: return Rise(Client.Keyboard.Q); case 82: return Rise(Client.Keyboard.R); case 83: return Rise(Client.Keyboard.S); case 84: return Rise(Client.Keyboard.T); case 85: return Rise(Client.Keyboard.U); case 86: return Rise(Client.Keyboard.V); case 87: return Rise(Client.Keyboard.W); case 88: return Rise(Client.Keyboard.X); case 89: return Rise(Client.Keyboard.Y); case 90: return Rise(Client.Keyboard.Z); //////////////////////////////////////////////////////////////////////////// case 91: //case 219: // opera return Rise(Client.Keyboard.LeftWindow); case 92: return Rise(Client.Keyboard.RightWindow); case 93: return Rise(Client.Keyboard.ContextMenu); //////////////////////////////////////////////////////////////////////////// case 96: return Rise(Client.Keyboard.NumPad0); case 97: return Rise(Client.Keyboard.NumPad1); case 98: return Rise(Client.Keyboard.NumPad2); case 99: return Rise(Client.Keyboard.NumPad3); case 100: return Rise(Client.Keyboard.NumPad4); case 101: return Rise(Client.Keyboard.NumPad5); case 102: return Rise(Client.Keyboard.NumPad6); case 103: return Rise(Client.Keyboard.NumPad7); case 104: return Rise(Client.Keyboard.NumPad8); case 105: return Rise(Client.Keyboard.NumPad9); //////////////////////////////////////////////////////////////////////////// case 106: return Rise(Client.Keyboard.Multiply); case 107: return Rise(Client.Keyboard.Add); case 109: return Rise(Client.Keyboard.Subtract); case 110: return Rise(Client.Keyboard.DecimalPoint); case 111: return Rise(Client.Keyboard.Divide); //////////////////////////////////////////////////////////////////////////// case 112: return Rise(Client.Keyboard.F1); case 113: return Rise(Client.Keyboard.F2); case 114: return Rise(Client.Keyboard.F3); case 115: return Rise(Client.Keyboard.F4); case 116: return Rise(Client.Keyboard.F5); case 117: return Rise(Client.Keyboard.F6); case 118: return Rise(Client.Keyboard.F7); case 119: return Rise(Client.Keyboard.F8); case 120: return Rise(Client.Keyboard.F9); case 121: return Rise(Client.Keyboard.F10); case 122: return Rise(Client.Keyboard.F11); case 123: return Rise(Client.Keyboard.F12); //////////////////////////////////////////////////////////////////////////// case 144: return Rise(Client.Keyboard.NumLock); case 145: return Rise(Client.Keyboard.ScrollLock); case 186: case 59: // opera & firefox return Rise(Client.Keyboard.SemiColon); case 187: case 61: // opera //case 107: //firefox return Rise(Client.Keyboard.Equal); case 188: return Rise(Client.Keyboard.Camma); case 189: return Rise(Client.Keyboard.Dash); case 190: return Rise(Client.Keyboard.Period); case 191: return Rise(Client.Keyboard.Slash); case 192: return Rise(Client.Keyboard.GraveAccent); case 219: return Rise(Client.Keyboard.OpenBracket); case 220: return Rise(Client.Keyboard.BackSlash); case 221: return Rise(Client.Keyboard.CloseBracket); case 222: return Rise(Client.Keyboard.SingleQuote); } };
// ctrl + s Client.Keyboard.S = function (e) { if (e.ctrl) { // انجام عملیات موردنظر } return true; }
- وقتی دیتایی داریم که به تکرار از آن در برنامه استفاده میکنیم.
- وقتی بعد از گرفتن دیتایی از دیتابیس، محاسباتی بر روی آن انجام میدهیم و پاسخ نهایی محاسبه را به کاربر نمایش میدهیم، میتوانیم یکبار پاسخ را کش کنیم تا از محاسبهی هر بارهی آن جلوگیری شود.
- سخت افزاری که برای کش استفاده میکنیم یعنی Ram، بسیار گرانتر از دیتابیس برای ما تمام میشود؛ چرا که محدود است.
- اگر همه دیتاهارا کش کنید، عمل سرچ میان آن زمان بیشتری خواهد برد.
این روش تا زمانیکه برنامهی ما برای اجرا شدن، تنها از یک سرور استفاده کند، بهترین انتخاب خواهد بود؛ چرا که به دلیل نزدیک بودن، سریعترین بازخورد را نیز به درخواستها ارائه میدهد.
اما شرایطی را فرض کنید که برنامه از چندین سرور برای اجرا شدن استفاده میکند و به طبع هر سرور درخواستهای خودش را داراست که ما باید برای هر یک بصورت جداگانهای یک کش In-Memory را در حافظه Ram هرکدام ایجاد کنیم.
فرض کنید دیتای ما 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 باشد. بخشی از دیتا در Server 1 کش میشود (1 , 3 , 5 , 9) و بخشی دیگر در Server 2 کش خواهد شد (2 , 4 , 6 ,7 , 8 , 10).
در اینجا مشکلات و ضعف هایی به وجود خواهد آمد :
- برای مثال اگر Server 1 به هر دلیلی از بین برود یا Down شود، اطلاعات کش درون آن نیز پاک خواهد شد و بعد از راه اندازی باید همه آن را دوباره از دیتابیس بخواند.
- هر کدام از سرورها کشهای جدایی دارند و باهم Sync نیستند و امکان وجود یک دادهی حیاتی در یکی و عدم وجود آن در دیگری، بالاست. فرض کنید برنامه برای هر درخواست، نیاز به اطلاعات دسترسی کاربری را دارد. دسترسیهای کاربر، در Server 1 کش شده، اما در Server 2 موجود نیست. در Server 2 به دلیل عدم وجود این کش، برنامه برای درخواستهای معمول خود و چک کردن دسترسی کاربر یا باید هربار به دیتابیس درخواستی را ارسال کند که این برخلاف خواسته ماست و یا باید دیتای مربوط به دسترسیهای کاربر را بعد از یکبار درخواست، از دیتابیس در خودش کش کند که اینهم دوباره کاری به حساب میاید و دوبار کش کردن یک دیتا، امر مطلوبی نخواهد بود.
روش هایی وجود دارد که بتوان از سیستم Local Caching در حالت چند سروری هم استفاده کرد و این مشکلات را از بین برد، اما روش استاندارد در حالت چند سروری، استفاده از Distributed Cacheها است.
روش دوم : Distributed Caching
در این روش برنامهی ما برای اجرا شدن از چندین سرور شبکه شده به هم، در حال استفاده هست و Cache برنامه، توسط سرورها به اشتراک گذاشته شده.
در این حالت سرورهای ما از یک کش عمومی استفاده میکنند که مزایای آن شامل :
■ درخواستها به چندین سرور مختلف از هم ارسال شده، اما دیتای کش بصورت منسجم در هریک وجود خواهد داشت.
■ با خراب شدن یا Down شدن یک سرور، کش موجود در سرورهای دیگر پاک نمیشود و کماکان قابل استفاده است.
■ به حافظه Ram یک سرور محدود نیست و مشکلات زیادی همچون کمبود سخت افزاری و محدودیتهای حافظهی Ram را تا حد معقولی کاهش میدهد.
طریقه استفاده از Cache در Asp.Net Core :
- بر خلاف ASP.NET web forms و ASP.NET MVC در نسخههای Core به بعد، Cache بصورت از پیش ثبت شده، وجود ندارد. کش در Asp.Net Core با فراخوانی سرویسهای مربوطهی آن قابل استفاده است و نیاز است قبل از استفاده، سرویس آن را در کلاس Startup برنامه فراخوانی کنید.
public void ConfigureServices(IServiceCollection services) { services.AddMvc(); services.AddMemoryCache(); }
- اینترفیس IMemoryCache از سیستم تزریق وابستگیها در Core استفاده میکند و برای استفاده از اینترفیس آن، پس از اضافه کردن MemoryCache به Startup ، باید در کنترلر، عمل تزریق وابستگی (DI) را انجام دهید؛ سپس متدهای مورد نیاز برای کش، در دسترس خواهد بود.
public class HomeController : Controller { private readonly IMemoryCache _cache; public HomeController(IMemoryCache cache) { _cache = cache; } .... }
- برای ذخیرهی کش میتوانید از متد Set موجود در این اینترفیس استفاده کنید.
public IActionResult Set() { _cache.Set("CacheKey", data , TimeSpan.FromDays(1)); return View(); }
در پارامتر اول این متد (CacheKey)، یک کلید، برای اطلاعاتی که میخواهیم کش کنیم قرار میدهیم. دقت کنید که این کلید، شناسهی دیتای شماست و باید طوری آن را در نظر گرفت که با صدا زدن این کلید از سرویس کش، همان دیتای مورد نظر را برگشت دهد (هر Object دیتا، باید کلید Unique خود را داشته باشد).
در پارامتر دوم، دیتای مورد نظر را که میخواهیم کش کنیم، به متد میدهیم و در پارامتر سوم نیز زمان اعتبار و تاریخ انقضای دیتای کش شده را وارد میکنیم؛ به این معنا که دیتای کش شده، بعد از مدت زمان گفته شده، از حافظه کش(Ram) حذف شود و برای دسترسی دوباره و کش کردن دوباره اطلاعات، نیاز به خواندن مجدد از دیتابیس باشد.
- برای دسترسی به اطلاعات کش شده میتوانید از متد Get استفاده کنید.
public IActionResult Get() { string data = _cache.Get("CacheKey"); return View(data); }
تنها پارامتر ورودی این متد، کلید از قبل نسبت داده شده به اطلاعات کش هست که با استفاده از یکسان بودن کلید در ورودی این متد و کلید Set شده از قبل در حافظه Ram، دیتا مربوط به آن را برگشت میدهد.
- متد TryGetValue برای بررسی وجود یا عدم وجود یک کلید در حافظه کش هست و یک Boolean را خروجی میدهد.
public IActionResult Set() { DateTime data; // Look for cache key. if (!_cache.TryGetValue( "CacheKey" , out data)) { // Key not in cache, so get data. data= DateTime.Now; // Save data in cache and set the relative expiration time to one day _cache.Set( "CacheKey" , data, TimeSpan.FromDays(1)); } return View(data); }
این متد ابتدا بررسی میکند که کلیدی با نام "CacheKey" وجود دارد یا خیر؟ در صورت عدم وجود، آن را میسازد و دیتای مورد نظر را به آن نسبت میدهد.
- با استفاده از متد GetOrCreate میتوانید کار متدهای Get و Set را باهم انجام دهید و در یک متد، وجود یا عدم وجود کش را بررسی و در صورت وجود، مقداری را return و در صورت عدم وجود، ابتدا ایجاد کش و بعد return مقدار کش شده را انجام دهید.
public IActionResult GetOrCreate() { var data = _cache.GetOrCreate( "CacheKey" , entry => entry.SlidingExpiration = TimeSpan.FromSeconds(3); return View(data); }); return View(data); }
- برای مدیریت حافظهی Ram شما باید یک Expiration Time را برای کشهای خود مشخص کنید؛ تا هم حافظه Ram را حجیم نکنید و هم در هر بازهی زمانی، دیتای بروز را از دیتابیس بخوانید. برای این کار optionهای متفاوتی از جمله absolute expiration و sliding expiration وجود دارند.
در اینجا absolute expiration به این معنا است که یک زمان قطعی را برای منقضی شدن کشها مشخص میکند؛ به عبارتی میگوییم کش با کلید فلان، در تاریخ و ساعت فلان حذف شود. اما در sliding expiration یک بازه زمانی برای منقضی شدن کشها مشخص میکنیم؛ یعنی میگوییم بعد از گذشت فلان دقیقه از ایجاد کش، آن را حذف کن و اگر در طی این مدت مجددا خوانده شد، طول مدت زمان آن تمدید خواهد شد.
این تنظیمات را میتوانید در قالب یک option زمان Set کردن یک کش، به آن بدهید.
MemoryCacheEntryOptions options = new MemoryCacheEntryOptions(); options.AbsoluteExpiration = DateTime.Now.AddMinutes(1); options.SlidingExpiration = TimeSpan.FromMinutes(1); _cache.Set("CacheKey", data, options );
در مثال بالا هردو option اضافه شده یک کار را انجام میدهند؛ با این تفاوت که absolute expiration تاریخ now را گرفته و یک دقیقه بعد را به آن اضافه کرده و تاریخ انقضای کش را با آن تاریخ set میکند. اما sliding expiration از حالا بمدت یک دقیقه اعتبار دارد.
- یکی از روشهای مدیریت حافظه Ram در کشها این است که برای حذف شدن کشها از حافظه، اولویت بندیهایی را تعریف کنید. اولویتها در چهار سطح قابل دسترسی است:
- NeverRemove = 3
- High = 2
- Normal = 1
- Low = 0
این اولویت بندیها زمانی کاربرد خواهند داشت که حافظه اختصاصی Ram، برای کشها پر شده باشد و در این حالت سیستم کشینگ بصورت خودمختار، کشهای با الویت پایین را از حافظه حذف میکند و کشهای با الویت بیشتر، در حافظه باقی میمانند. این با شماست که الویت را برای دیتاهای خود تعیین کنید؛ پس باید با دقت و فکر شده این کار را انجام دهید.
MemoryCacheEntryOptions options = new MemoryCacheEntryOptions(); // Low / Normal / High / NeverRemove options.Priority = CacheItemPriority.High; cache.Set("CacheKey", data, options);
به این صورت میتوانید الویتهای متفاوت را در قالب option به کشهای خود اختصاص دهید.
در این مقاله سعی شد مفاهیم اولیه Cache، طوری گفته شود، تا برای افرادی که میخواهند به تازگی این سیستم را بیاموزند و در پروژههای خود استفاده کنند، کاربردی باشد و درک نسبی را نسبت به مزایا و محدودیتهای این سیستم بدست آورند.
در قسمت دوم همین مقاله بطور تخصصیتر به این مبحث میپردازیم و یک پکیج آماده را معرفی میکنیم که خیلی راحتتر و اصولیتر کش را برای ما پیاده سازی میکند.
ASP.NET MVC #11
MyViewModel دوم مطرح شده: این صحیح است و فیلدهایی که در آن مشاهده میکنید بر اساس ساختار View (ایی فرضی جهت توضیح بحث) تعریف شدن و تناظر یک به یک وجود دارد.
- بله ViewModel ساختار فیلدهای تشکیل دهندهاش رو (یعنی نحوه تعریف خواص/طراحی خودش رو) از کنترلر نمیگیره (بر مبنای ساختار View متناظر تعریف میشه). اما در کنترلر هست که مقدار دهی خواهد شد و نهایتا در اختیار View قرار میگیره. بنابراین فرق است بین طراحی ساختار یک کلاس بر اساس Viewایی متناظر و در ادامه نحوه مقدار دهی آن در یک کنترلر و نهایتا ارسال آن به View جهت استفاده. وجود منطق در اینجا یعنی پیاده سازی مستقیم؛ نه اینکه مجاز نیستیم یک نام ترکیبی بر اساس نیاز View تعریف/طراحی کنیم.
همچنین وجود فیلدهای محاسباتی خیلی جزئی با پیاده سازی اندک هیچ ایرادی در ViewModel ندارد و بسیار هم مورد استفاده است. مثلا جمع زدن نامها؛ تبدیل تاریخ، محاسبه درصد مالیات و موارد جزئی از این دست که مستقیما در View صرفا جهت نمایش استفاده میشوند. منطقها و پیاده سازیهای بیش از یک سطر رو به لایه سرویس منتقل، در کنترلر فراخوانی و سپس در وهله سازی ViewModel ازش استفاده کنید.
NoSQL ؟
این موضوع است را مطالعه کنید.
NoSQL یک مفهوم عام است و تعریف ساده آن "پایگاه داده بدون SQL است". به این معنی که در آن خبری از جدول ها، روابط بین آنها و ... نیست!
- اما چرا باید با وجود اینکه SQL به اغلب نیازهای ما پاسخ داده است، باید سراغ تکنولوژیهای دیگر رفت؟
- وقتی نگاهی به لیست شرکتهای بزرگی میاندازیم که جز مشتریان پر و پا قرص
NoSQL هستند( + و + )، تعجب میکنیم! آیا آنها از قدرت و قابلیتهای SQL
بی خبراند؟
NoSQL بر اساس نحوه پیاده سازی اش دسته بندی شده است؛ که مهمترین آنها در زیر آمده است :
- Wide Column Store
- Document Store
- Key Value / Tuple Store
- Graph Databases
- Multimodel Databases
- Object Databases
انتخاب روش؛ یک مثال ساده :
فرض کنید روال استخدام نیروی کار جدید در یک سازمان، از قرار زیر باشد:
- ثبت مشخصات فردی
- ارائه مدارک تحصیلی
- شرکت در آزمون استخدامی
- شرکت در مصاحبه ( درصورت قبول شدن در آزمون )
- شرکت در دوره آموزشی ( در صورت قبول شدن در مصاحبه )
روش دوم، تهیه یک پرونده برای هر شخص و نگهداری اسناد مربوط به شخص ( در هر مرحله ) است.
خوش بختانه تعداد پایگاههای داده ای که بر اساس تکنولوژی Document Store پیاده سازی شده اند، زیاد است و از قدرتمندترین آنها میتوان به MongoDB ، CouchDB و RavenDB اشاره کرد. هرکدام از این انتخابها مزایا و معایبی دارند که باید با توجه به نیاز خود، مقایسه ای انجام داده و بهترین را انتخاب کنید.
انتخاب من RavenDB بوده است و دلایل آن :
- بر اساس زبان سی شارپ نوشته شده است و همچنین با LINQ خیلی خوب کار میکند.
- Transaction را پشتیبانی میکند.
- اساس ذخیره سازی آن JSON است.
- محیط Management Studio کاربر پسندی دارد.
نقطه آغازین بحث بعد RavenDB خواهد بود که Bryan Wheeler (مدیر توسعه بسترهای نرم افزاری در msn) در باره آن گفته :
RavenDB
just rocked my world. It’s extremely approachable, even for
non-database guys – it took me less than 30 minutes to get up and
running
خوشحال میشوم، نظرات و تجربیات شما را در رابطه با NoSQL بدانم.
- در این مورد DbContext در همان پرسش و پاسخهای قسمت 12 سری EF بحث شده. اینجا
برای تکرار:
«... در یک برنامه مبتنی بر MVVM، مدیریت طول عمر یک context در طول عمر ViewModel برنامه است. در یک برنامه ویندوزی تا زمانیکه یک فرم باز است، اشیاء آن تخریب نخواهند شد. بنابراین مدیریت context در برنامههای ویندوزی «دستی» است. در زمان شروع فرم context شروع خواهد شد، زمان تخریب/بستن آن، با بستن یا dispose یک context، خودبخود اتصالات هم قطع خواهند شد.
بنابراین در برنامههای وب «context/session per http request» داریم؛ در برنامههای ویندوزی «context per operation or per form». یعنی میتونید بسته به معماری برنامه ویندوزی خود، context را در سطح یک فرم تعریف کنید و مدیریت؛ و یا در سطح یک عملیات کوتاه مانند یک کلیک ...»
راهنمای نوشتن رزومه برای مامور «کِی»
فیلم مردان سیاهپوش رو دیدین؟ مامور «دی» و مامور «کِی» همکارن. مامور «دی» سرعتعملش پایین اومده و دیگه از عملکردش راضی نیست. خودش رو بازنشسته میکنه. حالا مامور «کِی» نیاز به یه همکار جدید داره. کسی که قراره بشه مامور «جِی»! خب حالا سوال اینجاست که اگه قرار باشه برای مامور «کِی» رزومه بنویسیم و اون موقعیت رو به دست بیاریم باید چی کار کنیم؟
ساخت ربات تلگرامی با #C
اما در چت آی دی همش به مشکل میخورم
لینکی که ارسال میکنم :
https://api.telegram.org/bot72988154:AAFRbBFec9guVnt8Hq0STMFNyfnKQrPZNwk/sendMessage?text=message&chat_id=72988154%27
و خطای دریافتی :
{"ok":false,"error_code":403,"description":"Error: Bot can't send messages to bot"}
چت آی دی باید چی باشه؟