ایجاد جداول توسط هر سیستم به صورت سفارشی سازی باشد. مثل پکیچ Identity که میشود سفارشی سازی کرد.
نظرات مطالب
ASP.NET MVC #11
امکان سفارشی سازی خروجی افزونهی Debug visualizer وجود دارد.
- روش اول: «آشنایی با ویژگی DebuggerDisplay در VS.Net»
- روش دوم: متد ToString شیء جاری را بازنویسی کنید. این روش دوم در مورد ساختار DateTime استفاده میشود که بر اساس فرهنگ جاری سیستم عمل میکند.
- روش اول: «آشنایی با ویژگی DebuggerDisplay در VS.Net»
- روش دوم: متد ToString شیء جاری را بازنویسی کنید. این روش دوم در مورد ساختار DateTime استفاده میشود که بر اساس فرهنگ جاری سیستم عمل میکند.
سلام
سپس دستور Truncate را روی جدول Table_3 اجرا کنید:
حال یک Sequence ایجاد کنید:
در ادامه محتویات جدول کپی را به جدول اصلی منتقل نمایید:
شما قادر نیستید یک فیلد Identity را بروز رسانی نمایید، دستور set insert_identity Tablename on به شما اجازه Insert به جدول بدون Identity را میدهد، برای اینکه بتوانید Gap مرتبط به فیلد Identity را در جدول برطرف کنید، در ابتدا از جدول مورد نظر خود یک کپی تهیه و جدول اصلی را Truncate کنید، سپس یک Sequencer ایجاد و محتویات جدول کپی را بوسیله Sequencer در جدول اصلی کپی نمایید.
فرض کنیم جدول اصلی Table_3 باشد، ابتدا آن را کپی میکنیم در جدولی به نام T
Select * into T from table_3
truncate table dbo.table_3
CREATE SEQUENCE testEventCounter AS int START WITH 1 INCREMENT BY 1 ;
SET IDENTITY_INSERT table_3 on INSERT INTO table_3 (ID, Descritp) SELECT NEXT VALUE FOR testEventCounter AS id , Descritp FROM T
راه دیگر این است که به جای استفاده از Identity از Sequence در فیلد خود استفاده نمایید، بصورت زیر :
CREATE TABLE Table3 ( ID int PRIMARY KEY CLUSTERED DEFAULT (NEXT VALUE FOR SequenceTest), De nvarchar(300) NULL ) ; GO
در هنگام ایجاد جدول Sequence را به فیلد ID ست کردیم.
حال هر زمانی که بخواهید میتوانید فیلد ID را مطابق Sequence خود بروز رسانی کنید:
Update table3 set id=(NEXT VALUE FOR testEventCounter )
موفق باشید و امیدوارم مفید واقع شده باشد
بازخوردهای پروژهها
راهنمایی در مورد چیدن اجزای گزارش
با عرض سلام من یک گزارشی طراحی کردم میخواستم اگر براتون مقدور هست فقط چک بکنید که اجزای گزارش را درست در صفحه قرار دادم یا نه. و اینکه در همین حین برای قسمت Footer یک مشکل دارم.
من قسمت بالای صفحه (تا جدول هزینه ها) درون Header قرار دادم و خود جدول رو با استفاده از MainTableColumns ایجاد کردم حال میخواهم قسمت پایین صفحه رو ایجاد بکنم. حال سوال من اینجاست که آیا قسمت پایین صفحه(آیتمهای بعد از جدول هزینه ها) باید در قسمت Footer گزارش قرار بگیرند؟ اگر جواب مثبت هست چگونه میتونم در قسمت Footer همانند قسمت Header جدول ایجاد بکنم که بتوانم این ساختار را ایجاد کنم.
ممنونم
من قسمت بالای صفحه (تا جدول هزینه ها) درون Header قرار دادم و خود جدول رو با استفاده از MainTableColumns ایجاد کردم حال میخواهم قسمت پایین صفحه رو ایجاد بکنم. حال سوال من اینجاست که آیا قسمت پایین صفحه(آیتمهای بعد از جدول هزینه ها) باید در قسمت Footer گزارش قرار بگیرند؟ اگر جواب مثبت هست چگونه میتونم در قسمت Footer همانند قسمت Header جدول ایجاد بکنم که بتوانم این ساختار را ایجاد کنم.
ممنونم
فرض کنید میخواهید برای بخشهایی از نرم افزاری که طراحی کردهاید ، امکانی را در نظر بگیرید که بتوانید زمانیکه نرم افزار در حال استفادهاست، قابلیتهایی از آنرا فعال یا غیرفعال نمایید؛ بدون اینکه نرم افزار از دسترس خارج شود. Feature Toggle که تحت عنوان Feature Flag هم شناخته میشود همین امکان را برای ما به ارمغان میآورد و ما را قادر میسازد تا قابلیتهایی را از نرم افزار، فعال یا غیرفعال کنیم، بدون اینکه نیاز باشد نرم افزار از دسترس مشتریان خارج شود و یا نیاز باشد نسخهی جدیدی از نرم افزار منتشر شود. برای مثال قابلیت ثبت نام کاربران را در بازههای خاصی غیرفعال کنیم و یا فرض کنید قابلیت جدیدی به نرم افزار اضافه کردهاید و میخواهید بعد از پابلیش، در یک بازه زمانی که نرم افزار شما بازدید کنندههای کمتری دارد، آنرا موقتا فعال کنید، نتیجه خروجی را ببینید و سپس آن را غیر فعال نمایید. در ادامه این مقاله سعی خواهیم کرد ابتدا با یک مثال ساده با این قابلیت آشنا شویم و سپس به معرفی یکی از کتابخانههای محبوب در این زمینه بپردازیم.
Feature Toggle چیزی بیشتر از یک دستور IF نیست، اگر شرط مورد نظر برقرار بود، کد را اجرا میکند، در غیر اینصورت از اجرای آن بخش صرف نظر میکند.
IF (currentYear<2023){ alert('Wear a mask!'); }
var showCoronaAlert=_cofiguration.GetValue<bool>("Features:showCoronaAlert"); // or read this from Database
If(showCoronaAlert){
alert(Wear a amask!);
}
در این روش بجای اینکه تاریخ را چک کنیم و بر اساس آن تصمیم بگیریم که آیا پیغامی نمایش داده شود یا نه، وضعیت نمایش آن را از فایل تنظیمات و یا دیتابیس خواندهایم. در این حالت دیگر نیازی به تغییر و انتشار نسخهی جدیدی از نرم افزار نیست و فقط کافیاست مقدار مربوط به نمایش پیغام را در دیتابیس و یا فایل تنظیمات، به روزسانی نماییم.
کتابخانه Microsoft.FeatureManagement توسط تیم اژور پیاده سازی و نوشته شدهاست و برای خواندن اطلاعات، از همان IConfiguration استفاده میکند که ما را قادر میسازد تنظیمات را از منابع مختلفی بخوانیم و همچنین قابلیتهای آن فراتر از تنظیم یک مقدار با true/false میباشد که در ادامه با بعضی از آنها آشنا خواهیم شد.
ابتدا نیاز هست این کتابخانه را به صورت زیر نصب نماییم :
Install-Package Microsoft.FeatureManagement
سپس نیاز هست در متد ConfigureService، سرویس مربوطه را اضافه نماییم :
using Microsoft.FeatureManagement; public void ConfigureServices(IServiceCollection services) { services.AddFeatureManagement(); }
این کتابخانه به صورت پیش فرض، اطلاعات featureها را از بخشی (section) تحت عنوان FeatureManagement از فایل appsetting.json میخواند. پس نیاز داریم این بخش را در appsetting.json تعریف نماییم ( لیست تمامی قابلیتهایی را که قصد داریم به صورت داینامیک فعال/غیرفعال کنیم، در این بخش اضافه خواهیم کرد):
"FeatureManagement": { }
اگر تمایل داشتید از اسم دیگری برای بخش تنظیمات، در فایل appsetting. json استفاده نمایید، میتوانید به صورت زیر این کار را انجام دهید :
public void ConfigureServices(IServiceCollection services) { services.AddFeatureManagement(Configuration.GetSection("MyFeatureManagement")) }
در این مقاله از همان اسم پیش فرض استفاده شده است.
افزودن یک قابلیت جدید
"FeatureManagement": { "MaskAlert":true }
public class HomeController : Controller { private readonly IFeatureManager _featureManager; public HomeController(IFeatureManager featureManager) { _featureManager = featureManager; } public async Task<IActionResult> Index() { if(await _featureManager.IsEnabledAsync("MaskAlert")) { // show messeage } return View(); } }
فعال سازی بر اساس تاریخ (TimeWindowsFilter)
یکی از قابلیتهای این کتابخانه، فعال سازی بر اساس بازه زمانی هست. اگر نیاز دارید یک قابلیت در یک بازهی خاص فعال شود، میتوانید از این قابلیت استفاده کنید. برای فعال سازی این امکان، باید فیلتر TimeWindowFilter را که به صورت توکار به همراه کتابخانه وجود دارد، به صورت زیر در متد configureServices ثبت نماییم:
public void ConfigureServices(IServiceCollection services) { services.AddFeatureManagement().AddFeatureFilter<TimeWindowFilter>(); }
"FeatureManagement": { "EmergencyBanner": { "EnabledFor": [ { "Name": "Microsoft.TimeWindow", "Parameters": { "Start": "01 Mar 2021 12:00:00 +00:00", "End": "01 Apr 2021 12:00:00 +00:00" } } ] } }
if(await _featureManager.IsEnabledAsync("EmergencyBanner")){ // show Emergency banner }
پارامترهای Start و End میتوانند به صورت تکی هم استفاده شوند؛ به این معنا که میتوانید فقط پارامتر start را مقدار دهی کنید و در این حالت از تاریخ مورد نظر به بعد، Feature مورد نظر فعال میباشد و یا اگر فقط پارامتر End مقدار دهی شود، Feature مورد نظر فقط تا تاریخ تعیین شده فعال هست و بعد از آن برای همیشه غیرفعال میشود.
در زیر، نمونهای از این حالت تنظیم شدهاست :
"FeatureManagement": { "EmergencyBanner": { "EnabledFor": [ { "Name": "Microsoft.TimeWindow", "Parameters": { "End": "01 Apr 2021 12:00:00 +00:00" } } ] } }
فیلترهای سفارشی
از دیگر مزایای این کتابخانه این هست که محدود به فیلترهای توکار خود آن نیستیم و امکان توسعه و نوشتن فیلترهای سفارشی را به ما میدهد. برای مثال اگر یک قابلیت را در نرم افزار پیاده سازی کردهایم که میخواهیم فقط بر روی مرورگرهای خاصی در دسترس باشد، میتوانیم به صورت زیر این کار را انجام دهیم:
ابتدا در appsetting.json قابلیت (Feature) مورد نظر را به صورت زیر تعریف میکنیم :
"FeatureManagement": { "ChatV2": { "EnabledFor": [ { "Name": "BrowserFilter", "Parameters": { "AllowedBrowsers": [ "Chrome" ] } } ] } }
[FilterAlias("BrowserFilter")] public class BrowserFilter:IFeatureFilter { private readonly IHttpContextAccessor _httpContextAccessor; public BrowserFilter(IHttpContextAccessor httpContextAccessor) { _httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException(nameof(httpContextAccessor)); } public Task<bool> EvaluateAsync(FeatureFilterEvaluationContext context) { var userAgent = _httpContextAccessor.HttpContext.Request.Headers["User-Agent"].ToString(); var settings = context.Parameters.Get<BrowserFilterSettings>(); return Task.FromResult(settings.AllowedBrowsers.Any(userAgent.Contains)); } }
public class BrowserFilterSettings { public string[] AllowedBrowsers { get; set; } }
IHttpContextAccessor
را هم ثبت نماییم: public void ConfigureServices(IServiceCollection services) { services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>(); services.AddFeatureManagement() .AddFeatureFilter<BrowserFilter>(); }
if(await _featureManager.IsEnabledAsync("ChatV2")){ // do something }
* از دیگر قابلیتهای این کتابخانه، فعال و غیر فعال کردن کنترلر و اکشن متدها بر اساس وضعیت Featureها میباشد که در بخش دوم این مقاله به توضیح این موارد خواهیم پرداخت.
اگر به امکانات مرورگرهای جدید دقت کرده باشید، امکان تعریف منبع جستجوی جدید، نیز برای آنها وجود دارد. برای نمونه تصاویر ذیل مرتبط به مرورگرهای فایرفاکس و کروم هستند:
این مرورگرها در صورتیکه پیاده سازی پروتکل Open Search را در سایت شما پیدا کنند، به صورت خودکار امکان افزودن آنرا به عنوان منبع جستجوی جدیدی جهت جعبه متنی جستجوی خود ارائه میدهند. در ادامه قصد داریم با جزئیات پیاده سازی آن آشنا شویم.
تهیه OpenSearchResult سفارشی
برنامه باید بتواند محتوای XML ایی ذیل را مطابق پروتکل Open Search به صورت پویا تهیه و در اختیار مرورگر قرار دهد:
به همین جهت کلاس OpenSearchResult ذیل تهیه شده است تا انجام آنرا با روشی سازگار با ASP.NET MVC سهولت بخشد:
کار این Action Result، تهیه محتوایی XML ایی مطابق نمونهای است که در ابتدای توضیحات ملاحظه نمودید. توضیحات خواص آن، در ادامه مطلب ارائه شدهاند.
تهیه OpenSearchController
در ادامه برای استفاده از Action Result سفارشی تهیه شده، نیاز است یک کنترلر را نیز به برنامه اضافه کنیم:
برای استفاده از OpenSearchResult به چند نکته باید دقت داشت:
الف) آدرسهای مطرح شده در آن باید مطلق باشند و نه نسبی. به همین جهت پارامتر protocol در اینجا ذکر شده است تا سبب تولید یک چنین آدرسهایی گردد.
ب) Url.Action ایی که در اینجا استفاده شده است مطابق تعاریف T4MVC است؛ ولی کلیات آن با نمونه پیش فرض ASP.NET MVC تفاوتی نمیکند. توسط T4MVC بجای ذکر نام اکشن متد و کنترلر مد نظر به صورت رشتهای، میتوان به صورت Strongly typed به این موارد ارجاع داد.
ج) تنها نکته مهم این کلاس، خاصیت SearchUrlTemplate است. قسمت انتهایی آن یعنی ={searchTerms} همیشه ثابت است. اما ابتدای این آدرس باید به کنترلر جستجوی شما که قادر است پارامتری را به شکل کوئری استرینگ دریافت کند، اشاره نماید.
د) FavIconUrl به آدرس یک آیکن در سایت شما اشاره میکند. برای نمونه ذکر favicon.ico پیش فرض سایت میتواند مفید باشد.
معرفی OpenSearchController به Header سایت
مرحله نهایی افزودن پروتکل Open search به سایت، مراجعه به فایل layout پروژه و افزودن link خاص فوق به آن است. در این لینک، href آن باید به مسیر کنترلر OpenSearchایی که در قسمت قبل تعریف کردیم، اشاره کند. این مسیر نیز باید مطلق باشد. به همین جهت پارامتر protocol آن مقدار دهی شده است.
این مرورگرها در صورتیکه پیاده سازی پروتکل Open Search را در سایت شما پیدا کنند، به صورت خودکار امکان افزودن آنرا به عنوان منبع جستجوی جدیدی جهت جعبه متنی جستجوی خود ارائه میدهند. در ادامه قصد داریم با جزئیات پیاده سازی آن آشنا شویم.
تهیه OpenSearchResult سفارشی
برنامه باید بتواند محتوای XML ایی ذیل را مطابق پروتکل Open Search به صورت پویا تهیه و در اختیار مرورگر قرار دهد:
<?xml version="1.0" encoding="UTF-8" ? /> <OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/"> <ShortName>My Site's Asset Finder</ShortName> <Description>Find all your assets</Description> <Url type="text/html" method="get" template="http://MySite.com/Home/Search/?q=searchTerms"/> <InputEncoding>UTF-8</InputEncoding> <SearchForm>http://MySite.com/</SearchForm> </OpenSearchDescription>
using System; using System.Text; using System.Web; using System.Web.Mvc; using System.Xml; namespace WebToolkit { public class OpenSearchResult : ActionResult { public string ShortName { set; get; } public string Description { set; get; } public string SearchForm { set; get; } public string FavIconUrl { set; get; } public string SearchUrlTemplate { set; get; } public override void ExecuteResult(ControllerContext context) { if (context == null) throw new ArgumentNullException("context"); var response = context.HttpContext.Response; writeToResponse(response); } private void writeToResponse(HttpResponseBase response) { response.ContentEncoding = Encoding.UTF8; response.ContentType = "application/opensearchdescription+xml"; using (var xmlWriter = XmlWriter.Create(response.Output, new XmlWriterSettings { Indent = true })) { xmlWriter.WriteStartElement("OpenSearchDescription", "http://a9.com/-/spec/opensearch/1.1/"); xmlWriter.WriteElementString("ShortName", ShortName); xmlWriter.WriteElementString("Description", Description); xmlWriter.WriteElementString("InputEncoding", "UTF-8"); xmlWriter.WriteElementString("SearchForm", SearchForm); xmlWriter.WriteStartElement("Url"); xmlWriter.WriteAttributeString("type", "text/html"); xmlWriter.WriteAttributeString("template", SearchUrlTemplate); xmlWriter.WriteEndElement(); xmlWriter.WriteStartElement("Image"); xmlWriter.WriteAttributeString("width", "16"); xmlWriter.WriteAttributeString("height", "16"); xmlWriter.WriteString(FavIconUrl); xmlWriter.WriteEndElement(); xmlWriter.WriteEndElement(); xmlWriter.Close(); } } } }
تهیه OpenSearchController
در ادامه برای استفاده از Action Result سفارشی تهیه شده، نیاز است یک کنترلر را نیز به برنامه اضافه کنیم:
using System.Web.Mvc; namespace Readers { public partial class OpenSearchController : Controller { public virtual ActionResult Index() { var fullBaseUrl = Url.Action(result: MVC.Home.Index(), protocol: "http"); return new OpenSearchResult { ShortName = ".NET Tips", Description = ".NET Tips Contents Search", SearchForm = fullBaseUrl, FavIconUrl = fullBaseUrl + "favicon.ico", SearchUrlTemplate = Url.Action(result: MVC.Search.Index(), protocol: "http") + "?term={searchTerms}" }; } } }
الف) آدرسهای مطرح شده در آن باید مطلق باشند و نه نسبی. به همین جهت پارامتر protocol در اینجا ذکر شده است تا سبب تولید یک چنین آدرسهایی گردد.
ب) Url.Action ایی که در اینجا استفاده شده است مطابق تعاریف T4MVC است؛ ولی کلیات آن با نمونه پیش فرض ASP.NET MVC تفاوتی نمیکند. توسط T4MVC بجای ذکر نام اکشن متد و کنترلر مد نظر به صورت رشتهای، میتوان به صورت Strongly typed به این موارد ارجاع داد.
ج) تنها نکته مهم این کلاس، خاصیت SearchUrlTemplate است. قسمت انتهایی آن یعنی ={searchTerms} همیشه ثابت است. اما ابتدای این آدرس باید به کنترلر جستجوی شما که قادر است پارامتری را به شکل کوئری استرینگ دریافت کند، اشاره نماید.
د) FavIconUrl به آدرس یک آیکن در سایت شما اشاره میکند. برای نمونه ذکر favicon.ico پیش فرض سایت میتواند مفید باشد.
معرفی OpenSearchController به Header سایت
<link href="@Url.Action(result: MVC.OpenSearch.Index(), protocol: "http")" rel="search" title=".NET Tips Search" type="application/opensearchdescription+xml" />
طبق این معرفی ، جنریکها باعث میشوند که نوع دادهای (data type) المانهای برنامه در زمان استفاده از آنها در برنامه مشخص شوند. به عبارت دیگر، جنریک به ما اجازه میدهد کلاسها یا متدهایی بنویسیم که میتوانند با هر نوع دادهای کار کنند.
در کد بالا کلاسی تعریف شده است که میتواند بر روی آرایههایی از نوع دادهای مختلف عملیات درج و حذف را انجام دهد. برای تعریف کلاس جنریک کافی است عبارت <T> بعد از نام کلاس خود اضافه کنید، سپس همانند سایر کلاسها از این نوع داده ای در کلاس استفاده کنید. در مثال بالا یک آرایه از نوع T تعریف شده است که این نوع، در زمان استفاده مشخص خواهد شد. (یعنی در زمان استفاده از کلاس مشخص خواهد شد که چه نوع آرایه ای ایجاد میشود)
زمانی که کد بالا اجرا میشود خروجی زیر بدست میآید:
نکاتی از جنریکها:
- برای به حداکثر رسانی استفاده مجدد از کد، type safety و کارایی است.
- بیشترین استفاده مشترک از جنریکها جهت ساختن کالکشن کلاسها (collection classes) است.
- تا حد ممکن از جنریک کالکشن کلاسها (generic collection classes) جدید فضای نام System.Collections.Generic بجای کلاسهایی مانند ArrayList در فضای نام System.Collections استفاده شود.
- شما میتوانید اینترفیس جنریک ، کلاس جنریک ، متد جنریک و عامل جنریک سفارشی خودتان تهیه کنید.
- جنریک کلاسها، ممکن است در دسترسی به متدهایی با نوع دادهای خاص محدود شود.
- بوسیله reflection، میتوانید اطلاعاتی که در یک جنریک در زمان اجرا (run-time) قرار دارد بدست آورید.
انواع جنریک ها:
- کلاسهای جنریک
- اینترفیسهای جنریک
- متدهای جنریک
- عاملهای جنریک
در قسمت اول به معرفی کلاس جنریک میپردازیم.
کلاسهای جنریک
کلاس جنریک یعنی کلاسی که میتواند با چندین نوع داده کار کند برای آشنایی با این نوع کلاس به کد زیر دقت کنید:
using System; using System.Collections.Generic; namespace GenericApplication { public class MyGenericArray<T> { // تعریف یک آرایه از نوع جنریک private T[] array; public MyGenericArray(int size) { array = new T[size + 1]; } // بدست آوردن یک آیتم جنریک از آرایه جنریک public T getItem(int index) { return array[index]; } // افزودن یک آیتم جنریک به آرایه جنریک public void setItem(int index, T value) { array[index] = value; } } }
در کد زیر نحوه استفاده از کلاس جنریک نشان داده شده است، همانطور که مشاهده میکنید نوع کلاس int و char در نظر گرفته شده است (نوع کلاس، زمان استفاده از کلاس مشخص میشود) و سپس آرایه هایی از نوع int و char ایجاد شده است و 5 آیتم از نوع int و char به آرایههای هم نوع افزوده شده است.
class Tester { static void Main(string[] args) { // تعریف یک آرایه از نوع عدد صحیح MyGenericArray<int> intArray = new MyGenericArray<int>(5); // افزودن اعداد صحیح به آرایه ای از نوع عدد صحیح for (int c = 0; c < 5; c++) { intArray.setItem(c, c*5); } // بدست آوردن آیتمهای آرایه ای از نوع عدد صحیح for (int c = 0; c < 5; c++) { Console.Write(intArray.getItem(c) + " "); } Console.WriteLine(); // تعریف یک آرایه از نوع کاراکتر MyGenericArray<char> charArray = new MyGenericArray<char>(5); // افزودن کاراکترها به آرایه ای از نوع کاراکتر for (int c = 0; c < 5; c++) { charArray.setItem(c, (char)(c+97)); } // بدست آوردن آیتمهای آرایه ای از نوع کاراکتر for (int c = 0; c< 5; c++) { Console.Write(charArray.getItem(c) + " "); } Console.WriteLine(); Console.ReadKey(); } }
0 5 10 15 20 a b c d e
اشتراکها
عرضه ASP.NET Identity 2.1.0-alpha1
خوشبختانه در این نگارش پیشنهاد متود الحاقی ()<GetUserId<T را داده بودم که اضافه شده است.
حالا با سفارشی سازی ASP.NET Identity، از این متود الحالی به راحتی میتوانیم UserId را بدست بیاوریم
User.Identity.GetUserId<int>();
در Asp.Net Identity نباید یک DbContext جداگانه ایجاد کنید. از همان ApplicationDbContext آن جهت اضافه کردن سایر مدلهای برنامه استفاده کنید؛ به همراه سفارشی سازی و توسعه آن. مابقی مسایل و نکات آن مانند سایر مباحث متداول EF Code first است و تفاوتی نمیکند.
در مقاله قبلی با یکی از کتابخانههای مدیریت دیتابیس sqlite آشنا شدیم و و یاد گرفتیم که چگونه یک دیتابیس جدید را بسازیم و اطلاعات را از آن دریافت کنیم. در این مقاله قصد داریم، بیشتر در مورد دستورات این کتابخانه بدانیم و بفهمیم که چگونه باید آنها را به کار بست.
دستورات بدون خروجی:
یک سری از دستورات هستند که خروجی ندارند و رکوردی را باز نمیگردانند و برای اجرای دستوراتی چون افزودن، به روزرسانی و حذف بسیار مناسبند. اجرای این دستورات را ما به متدی به نام run میسپاریم. در دفعه قبل که از این دستور استفاده کردیم، پارامتری برای تعیین کردن نداشت؛ ولی در این مقاله، دستور با پارامتر آن را اجرا میکنیم:
ابتدا کدهای زیر را به فایل html، برای درج رکورد جدید اضافه میکنیم:
دستورات بدون خروجی:
یک سری از دستورات هستند که خروجی ندارند و رکوردی را باز نمیگردانند و برای اجرای دستوراتی چون افزودن، به روزرسانی و حذف بسیار مناسبند. اجرای این دستورات را ما به متدی به نام run میسپاریم. در دفعه قبل که از این دستور استفاده کردیم، پارامتری برای تعیین کردن نداشت؛ ولی در این مقاله، دستور با پارامتر آن را اجرا میکنیم:
ابتدا کدهای زیر را به فایل html، برای درج رکورد جدید اضافه میکنیم:
First Name:<br/> <input type="text" id="txtfname" /><br/> Last Name:<br/> <input type="text" id=txtlname /><br/> Number:<br/> <input type="tel" id="txttel" /><br/> <button id="btnsubmit">Save</button><br/>
function GetValues() { let fname=$("#txtfname").val(); let lname=$("#txtlname").val() let tel=$("#txttel").val(); let row= { fname:fname, lname:lname, number:tel }; return row; }
$("#btnsubmit").click((e)=>{ e.preventDefault(); let row=GetValues(); //save in db //get last id let statement==db.prepare("select id from numbers order by id desc limit 1"); let lastRecord=statement.getAsObject({}).id; row.id=lastRecord++; let count=db.prepare("select count(*) as count from numbers order by id desc").getAsObject({}).count; statement.free(); let insertCommand="insert into numbers values(?,?,?,?)"; db.run(insertCommand,[row.id,row.fname,row.lname,String(row.number)]) let newcount=db.prepare("select count(*) as count from numbers order by id desc").getAsObject({}).count; SaveChanges(); //show in the table if(count<newcount) { AddToTable(row); } }); });
var statement= db.prepare("SELECT * FROM NUMBERS WHERE fname=@fname AND lname=@lname"); var result = statement .getAsObject({'@fname' :'ali', '@lname' : 'yeganeh'});
statement.bind(['hossein','yeganeh']);
متد step همانند متدهای next در cursor یا read در datareader عمل میکند و با هر بار صدا زدن، یک رکورد، به سمت جلو حرکت میکند. دریافت هر رکورد جاری توسط متد get و نوع خروجی آرایه انجام میشود:
while(statement.step()) { var rec=statement.get(); }
بعد از اینکه کارمان با آن تمام شد، برای پاکسازی حافظه از متد free استفاده میکنیم. در دستورات بعد، شیء statement را مستقیما مورد استفاده قرار دادهایم و توسط آن تعداد رکوردها را دریافت کردهایم. سپس با استفاده از متد run دستور درج را دادهایم. اینبار این متد را به شکل متفاوتی استفاده کردیم و به آن پارامتری هم دادیم. نحوه ارائه پارامتر به این متد، باید به صورت آرایه و به ترتیب علامتهای ؟ باشد. نهایتا با دریافت تعداد رکوردهای جاری و مقایسه با تعداد رکوردهای سابق متوجه میشویم که آیا رکوردی اضافه شده است یا خیر؟ در صورتی که اضافه شده است، باید رکورد جدید در جدول، توسط جی کوئری نمایش داده شود و تغییرات دیتابیس هم روی دیسک سخت ذخیره شوند. چون دیتابیس مورد استفاده به صورت in-memory یعنی مقیم در حافظه مورد استفاده قرار میگیرد، باید کل دیتابیس، بر روی دیسک سخت رونویسی شود. متد SaveChanges شامل کد زیر است که حاوی کد ارسال پیام به Main Thread یا Main Process میباشد تا در دیسک سخت بنویسد:
const {ipcRenderer} = require('electron'); function SaveChanges() { ipcRenderer.send("SaveToDb"); }
const {ipcMain} = require('electron'); ipcMain.on("SaveToDb", (event, arg) => { SaveToDb(); }); function SaveToDb() { var data=db.export(); var buffer=new Buffer(data); fs.writeFileSync(dbPath,buffer); }
ویرایش رکورد
ابتدا template string سطر جدول را به شکل زیر تغییر میدهیم:
function AddToTable(row) { let tableBody=$("#people"); let rowTemplate=`<tr><td>${row.fname}</td><td>${row.lname}</td><td>${row.number}</td><td><button class= "btn btn-success btnupdate" data-id="${row.id}" >Edit</button></td></tr>`; tableBody.append(rowTemplate); }
سپس در تگ اسکریپت، در رویداد ready جی کوئری، این دستورات را اضافه میکنیم:
$("#people").on('click','.btnupdate',function(e) { e.preventDefault(); row.id=$(this).data("id"); let row=GetValues(); db.run("UPDATE NUMBERS SET FNAME=?,LNAME=?,NUMBER=? WHERE ID=?",[row.fname,row.lname,row.number,row.id]); SaveChanges(); tr=$(this).closest("tr"); let column=0; tr.find("td").each(function(index) { oldRow=$(this); switch(column) { case 0: //fname oldRow.text(row.fname); break; case 1: //lname oldRow.text(row.lname); break; case 2: //number oldRow.text(row.number); break; } column++; }); });
ابتدای id ذخیره شده در المان و مقادیر جدید را دریافت میکنیم. با استفاده از متد run کوئری به روزرسانی را به همراه پارامترها ارسال میکنیم و نتیجه را بر روی دیسک سخت ذخیره میکنیم. از اینجا به بعد نقش جی کوئری پر رنگتر میشود و به خوبی میتوانیم اهمیت آن را درک کنیم. سطر دکمه جاری را پیدا میکنیم و مقادیر جدید را ستون به ستون تغییر میدهیم.
خواندن و بازگردانی رکوردها
در مقاله قبلی با دستور each آشنا شدیم که یک متد غیرهمزمان بود و نتیجه هر رکورد را با یک callback به ما بازگشت میداد. در اصل این متد شامل 4 پارامتر است: پارامتر اول آن، کوئری ارسالی است. پارامتر دوم آن، پارامتر کوئریها ، پارامتر سوم، تابع callback که به ازای هر رکورد اجرا میشود و پارامتر چهارم، تابع done می باشد. یعنی زمانی که کلیه رکوردها بازگشت داده شدند. شکل کامل آن به این صورت است:
db.each("SELECT name,age FROM users WHERE age >= $majority", {$majority:18}, function(row){console.log(row.name)}, function(){console.log("done");} );
در این مقاله با متد دیگری به نام exec نیز آشنا میشویم که بازگردانی مقادیر در آن به صورت همزمان صورت میگیرد و توانایی آن را دارد که چندین دستور select را بازگردانی کند. به عنوان مثال دستور زیر را در نظر بگیرید:
SELECT ID FROM NUMBERS;SELECT FNAME,LNAME FROM NUMBERS
[ {columns: ['id'], values:[[1],[2],[3]]}, {columns: ['fname','lname'], values:[['ali','yeganeh'],['hossein','yeganeh'],['mohammad','yeganeh']]} ]
var records=db.exec("select * from numbers"); let values=records[0].values; let length=values.length; for(let i=0;i<length;i++) { let object=values[i]; let row={ id:object[0], fname:object[1], lname:object[2], number:object[3] }; AddToTable(row); }