مثال دوم) به استفاده کننده از API کتابخانه خود، اجازه فرمول نویسی بدهید
برای نمونه مثال ساده زیر را درنظر بگیرید که در آن قرار است یک سری عدد که از منبع دادهای دریافت شدهاند، بر روی صفحه نمایش داده شوند:
public static void PrintNumbers() { var numbers = new[] { 1,2,3,5,7,90 }; // from a data source foreach(var item in numbers) { Console.WriteLine(item); } }
روال کار اکثر ابزارهای گزارشسازی موجود، ارائه یک زبان اسکریپتی جدید برای حل این نوع مسایل است. اما با استفاده از Func و ... روشهای Code first (بجای روشهای Wizard first)، خیلی از این رنج و دردها را میتوان سادهتر و بدون نیاز به اختراع و یا آموزش زبان جدیدی حل کرد:
public static void PrintNumbers(Func<int,string> formula) { var numbers = new[] { 1,2,3,5,7,90 }; // from a data source foreach(var item in numbers) { var data = formula(item); Console.WriteLine(data); } }
PrintNumbers(number => string.Format("{0:n0}",number));
PrintNumbers((number) =>{ return string.Format("{0:n0}",number); });
از این نوع طراحی، در ابزارها و کتابخانههای جدید گزارش سازی مخصوص ASP.NET MVC زیاد مشاهده میشوند.
مثال سوم) حذف کدهای تکراری برنامه
فرض کنید قصد دارید در برنامه وب خود مباحث caching را پیاده سازی کنید:
using System; using System.Web; using System.Web.Caching; using System.Collections.Generic; namespace WebToolkit { public static class CacheManager { public static void CacheInsert(this HttpContextBase httpContext, string key, object data, int durationMinutes) { if (data == null) return; httpContext.Cache.Add( key, data, null, DateTime.Now.AddMinutes(durationMinutes), TimeSpan.Zero, CacheItemPriority.AboveNormal, null); } } }
var item = httpContext.Cache[key]; if (item == null) { item = ReadDataFromDataSource(); if (item == null) return null; CacheInsert(httpContext, key, item, durationMinutes); }
میتوان در این الگوی تکراری، خواندن اطلاعات را از منبع داده، به یک Func واگذار کرد و به این صورت کدهای ما به نحو زیر بازسازی خواهند شد:
using System; using System.Web; using System.Web.Caching; using System.Collections.Generic; namespace WebToolkit { public static class CacheManager { public static void CacheInsert(this HttpContextBase httpContext, string key, object data, int durationMinutes) { if (data == null) return; httpContext.Cache.Add( key, data, null, DateTime.Now.AddMinutes(durationMinutes), TimeSpan.Zero, CacheItemPriority.AboveNormal, null); } public static T CacheRead<T>(this HttpContextBase httpContext, string key, int durationMinutes, Func<T> ifNullRetrievalMethod) { var item = httpContext.Cache[key]; if (item == null) { item = ifNullRetrievalMethod(); if (item == null) return default(T); CacheInsert(httpContext, key, item, durationMinutes); } return (T)item; } } }
var user = HttpContext.CacheRead( "Key1", 15, () => _usersService.FindUser(userId));
This article provides an introduction to a lightweight API for ASP.NET web services provided by DotNetify.SignalR NuGet package that can reduce a lot of boilerplate code when implementing real-time updates, and provide an example of how to create a real-time web component with VueJS without using Node.JS build.
تدارک ساختار ابتدایی این مطلب
در اینجا اینترفیسی را که بیانگر ساختار شیء شخص است، به صورت ذیل ایجاد میکنیم:
export interface Person { name: string; age: number; }
export class LinqTestsComponent { people: Person[] = [ { name: "User 4", age: 27 }, { name: "User 5", age: 42 }, { name: "User 6", age: 8 }, { name: "User 1", age: 20 }, { name: "User 2", age: 35 }, { name: "User 3", age: 78 } ]; }
همچنین سه متد ذیل را نیز برای لاگ کردن عنوان آزمایش، نمایش محتوای آرایهی اصلی و نمایش نتیجهی آزمایش، به این کلاس اضافه میکنیم:
logTitle(title: string) { console.log(`%c${title}`, "background: #222; color: #bada55"); } logOriginalArray() { console.log(`original this.people:${JSON.stringify(this.people)}`); } logResult(message: string, result: any) { console.log(`${message}:${JSON.stringify(result)}`); }
معادل متد Where در TypeScript
متد filter که جزو متدهای توکار ES5 است، میتواند معادلی برای متد Where، جهت فیلتر کردن عناصر بر اساس یک خاصیت، یا چندین خاصیت باشد:
equivalentToWhere() { const youngerThan40 = this.people.filter(person => person.age < 40); // ECMAScript 5 this.logResult("People younger than 40", youngerThan40); // Filtering on Multiple Criteria const youngsters = this.people.filter( person => person.age < 40 && person.name.toLocaleLowerCase().indexOf("user") !== -1); this.logResult("Users younger than 40", youngsters); }
People younger than 40:[ {"name":"User 4","age":27}, {"name":"User 6","age":8}, {"name":"User 1","age":20}, {"name":"User 2","age":35} ] Users younger than 40:[ {"name":"User 4","age":27}, {"name":"User 6","age":8}, {"name":"User 1","age":20}, {"name":"User 2","age":35} ]
معادل متد Any در TypeScript
متد some که جزو متدهای توکار ES5 است، میتواند معادلی برای متد Any باشد. اگر یکی از عناصر آرایه، بر اساس شرط تعیین شده یافت شود، این متد مقدار true را باز میگرداند:
equivalentToAny() { const anyUnder40 = this.people.some(person => person.age < 40); // ECMAScript 5 this.logResult("Are any people under 40?", anyUnder40); // true // Filtering on Criteria Matching any Object Properties const filterBy = "user"; const anyUsers = this.people.filter(person => Object.keys(person).some(property => { let value = (<any>person)[property]; if (typeof value === "string") { value = value.toLocaleLowerCase(); } return value.toString().indexOf(filterBy) !== -1; }) ); this.logResult("anyUsers", anyUsers); }
Are any people under 40?:true anyUsers:[ {"name":"User 4","age":27}, {"name":"User 5","age":42}, {"name":"User 6","age":8}, {"name":"User 1","age":20}, {"name":"User 2","age":35}, {"name":"User 3","age":78} ]
در مثال دوم، جستجویی بر روی تمام خواص شیء شخص انجام شدهاست. در اینجا توسط متد Object.keys، لیست خواص شیء یافت شدهاست. سپس بر روی این لیست توسط متد some، بررسی شدهاست که آیا خاصیت رشتهای وجود دارد که مساوی عبارت filterBy باشد؟ حاصل این بررسی به عنوان شرط متد filter جهت بازگشت آرایهی متناظری از اشخاص یافت شده، استفاده شدهاست.
معادل متد Contains در TypeScript
متد includes که جزو متدهای توکار ES7 است، میتواند معادلی برای متد Contains باشد و کار آن بررسی وجود عنصری در یک لیست است:
equivalentToContains() { const searchElement: Person = { name: "User 4", age: 27 }; const containsUser4 = this.people.includes(searchElement); // ECMAScript 2016 = ECMAScript 7 this.logResult("Contains searchElement", containsUser4); // false -> only compares by reference and not by value. const indexOfUser4 = this.people.indexOf(searchElement); // ECMAScript 5 this.logResult("indexOfUser4", indexOfUser4); // -1 -> only compares by reference and not by value. const stringifiedObj = JSON.stringify(searchElement); const includesUser4 = this.people.some(person => JSON.stringify(person) === stringifiedObj); this.logResult("includesUser4", includesUser4); // true -> compares by by value. }
یکی از روشهای مقایسهی بر اساس تمام مقادیر خواص یک شیء، استفاده از متد JSON.stringify است که اگر آنرا با متد some ترکیب کنیم، میتوان به نتیجهی مطلوبی رسید:
Contains searchElement:false indexOfUser4:-1 includesUser4:true
معادل متد All در TypeScript
متد every که جزو متدهای توکار ES5 است، میتواند معادلی برای متد All باشد و کار آن بررسی صحت شرط اعمالی، بر روی تک تک عناصر لیست است. اگر این بررسی با موفقیت صورت گرفت، مقدار true را بازگشت میدهد:
equivalentToAll() { const allUnder30 = this.people.every(person => person.age < 30); // ECMAScript 5 this.logResult("Are all people under 30?", allUnder30); // false }
Are all people under 30?:false
معادل متدهای First و FirstOrDefault در TypeScript
میتوان از متدهای filter و یا find بومی ES5 و ES 6 برای شبیه سازی متدهای First (یافتن اولین عنصر درخواستی در یک لیست) و FirstOrDefault استفاده کرد:
equivalentToFirstOrDefault() { const vahidOrDefault = this.people.filter(item => item.name === "Vahid")[0] || null; // ECMAScript 5 this.logResult("vahidOrDefault", vahidOrDefault); const user1OrDefault = this.people.find(item => item.name === "User 1") || null; // ECMAScript 2015 = ECMAScript 6 this.logResult("user1OrDefault", user1OrDefault); }
معادل متد FindIndex در TypeScript
متد findIndex که جزو متدهای توکار ES6 است، میتواند معادلی برای متد FindIndex در جهت یافتن ایندکس عنصری در یک لیست، بر اساس شرط درخواستی، باشد.
equivalentToFindIndex() { const index = this.people.findIndex(person => person.age === 8); // ECMAScript 2015 = ECMAScript 6 this.logResult("index of the user with age 8", index) }
index of the user with age 8:2
معادل متد Select در TypeScript
متد map که جزو متدهای توکار ES5 است، میتواند معادلی برای متد Select، برای تغییر شکل نهایی خروجی یک لیست باشد:
equivalentToSelect() { const names = this.people.map(person => person.name); // ECMAScript 5 this.logResult("Selected the names of people", names); }
Selected the names of people:["User 4","User 5","User 6","User 1","User 2","User 3"]
معادل متد Aggregate در TypeScript
متد reduce که جزو متدهای توکار ES5 است، میتواند شبیه به متد Aggregate عمل کند و لیستی از عناصر را به یک مقدار کاهش دهد:
equivalentToAggregate() { // ECMAScript 5 const aggregate = this.people.reduce((person1, person2) => { return { name: "", age: person1.age + person2.age }; }); this.logResult("Aggregate age", aggregate.age); // { age: 210 } }
Aggregate age:210
معادل متد ForEach در TypeScript
متد forEach که جزو متدهای توکار ES5 است، میتواند معادلی برای متد ForEach باشد که روشی functional برای پیمودن عناصر یک لیست است:
equivalentToForEach() { // ECMAScript 5 this.people.forEach(person => { this.logResult("person", person); }); }
person:{"name":"User 4","age":27} person:{"name":"User 5","age":42} person:{"name":"User 6","age":8} person:{"name":"User 1","age":20} person:{"name":"User 2","age":35} person:{"name":"User 3","age":78}
معادل متد OrderBy در TypeScript
متد sort که جزو متدهای توکار ES5 است، میتواند معادلی برای متد OrderBy باشد که جهت مرتب سازی عناصر یک لیست از آن استفاده میشود:
// ECMAScript 5 let orderedByAgeAscending = this.people.sort((person1, person2) => { const a = person1.age; const b = person2.age; return a > b ? 1 : -1; }); this.logResult("Ordered by age ascending", orderedByAgeAscending);
- مساوی صفر باشد، تغییری را به وجود نمیآورد.
- کمتر از صفر باشد، اولین عنصر را قبل از دومین عنصر قرار میدهد.
- بیشتر از صفر باشد، دومین عنصر را پس از اولین عنصر قرار میدهد.
منطق مقایسهی فوق را به صورت ذیل نیز میتوان خلاصه کرد:
orderedByAgeAscending = this.people.sort((person1, person2) => person1.age - person2.age); this.logResult("Ordered by age ascending", orderedByAgeAscending);
Ordered by age ascending:[ {"name":"User 6","age":8}, {"name":"User 1","age":20}, {"name":"User 4","age":27}, {"name":"User 2","age":35}, {"name":"User 5","age":42}, {"name":"User 3","age":78} ]
const orderedByName = this.people.sort((person1, person2) => { // name1.localeCompare(name2) // is case insensitive // or use toUpperCase() to ignore character casing const name1 = person1.name.toUpperCase(); const name2 = person2.name.toUpperCase(); return name1 > name2 ? 1 : -1; }) this.logResult("Ordered by name", orderedByName); this.logOriginalArray();
Ordered by name:[ {"name":"User 1","age":20}, {"name":"User 2","age":35}, {"name":"User 3","age":78}, {"name":"User 4","age":27}, {"name":"User 5","age":42}, {"name":"User 6","age":8} ] original this.people:[ {"name":"User 1","age":20}, {"name":"User 2","age":35}, {"name":"User 3","age":78}, {"name":"User 4","age":27}, {"name":"User 5","age":42}, {"name":"User 6","age":8} ]
امکان ترکیب زنجیروار متدهای کار بر روی لیستها در TypeScript
همانند LINQ، در اینجا نیز میتوان متدهای فوق را به صورت زنجیروار بر روی یک لیست فراخوانی و اجرا کرد:
chainFunctionCalls() { const namesOfPeopleOver30OrderedDesc = this.people .filter(person => person.age > 30) .map(person => person.name) .sort((name1, name2) => { // name1.localeCompare(name2) // is case insensitive // or use toUpperCase() to ignore character casing name1 = name1.toUpperCase(); name2 = name2.toUpperCase(); return name2 > name1 ? 1 : -1; }); this.logResult("the names of all people over 30 ordered by name descending", namesOfPeopleOver30OrderedDesc); }
the names of all people over 30 ordered by name descending:["User 5","User 3","User 2"]
معادل متد Skip در TypeScript
متد splice که جزو متدهای توکار ES5 است، میتواند شبیه به متد Skip عمل کند. این متد آرایهای را بازگشت میدهد که حاوی عناصری است که پس از تعداد ذکر شده، در آرایهی اصلی وجود دارند:
equivalentToSkip() { const skip2 = this.people.splice(2); // ECMAScript 5 this.logResult("skip2 -> the deleted elements", skip2); this.logOriginalArray(); }
skip2 -> the deleted elements:[ {"name":"User 3","age":78}, {"name":"User 4","age":27}, {"name":"User 5","age":42}, {"name":"User 6","age":8} ] original this.people:[ {"name":"User 1","age":20}, {"name":"User 2","age":35} ]
When I tell my fellow computer scientists or software developers that I'm interested in philosophy of science, they first look a bit confused, then we have a really interesting discussion about it and then they ask me for some interesting books they could read about it. Given that Christmas is just around the corner and some of the readers might still be looking for a good present to get, I thought that now is the perfect time to turn my answer into a blog post!
پیشنیاز : «تکرار خودکار سرستونهای یک جدول در صفحات مختلف، توسط iTextSharp»
همانطور که در مطلب پیشنیاز عنوان شده ذکر گردید، iTextSharp امکان درج خودکار header و footer به علاوه محاسبه خودکار تعداد ردیفهای یک جدول در یک صفحه را بر اساس طول و اندازه محتوای هر ردیف، دارد. برای مثال یک صفحه ممکن است 2 ردیف شود و یک صفحه 20 ردیف. تمام اینها را به صورت خودکار محاسبه میکند و بسیار عالی است. (این امکان مهمی است که خیلی از ابزارهای گزارشگیری موجود هنوز با آن مشکل دارند)
اما اگر فرض را بر این بگذاریم که اندازه سلولها و در نتیجه طول هر ردیف ثابت است و مثلا تمام صفحات نهایتا از یک تعداد ردیف مشخص تشکیل خواهند شد، خاصیتی را به نام number of rows یا rows count و امثال آنرا ندارد که مثلا به آن گفت، من در هر صفحه فقط 5 ردیف را میخواهم نمایش دهم و نه 20 ردیف را.
روش حل این مساله را در ادامه ملاحظه خواهید کرد و یک نکتهی خیلی ساده و مستند نشده دارد!
using System.Diagnostics;
using System.IO;
using iTextSharp.text;
using iTextSharp.text.pdf;
namespace RowsCountSample
{
class Program
{
static void Main(string[] args)
{
using (var pdfDoc = new Document(PageSize.A4))
{
var pdfWriter = PdfWriter.GetInstance(pdfDoc, new FileStream("Test.pdf", FileMode.Create));
pdfDoc.Open();
var table1 = new PdfPTable(3);
table1.HeaderRows = 2;
table1.FooterRows = 1;
//header row
var headerCell = new PdfPCell(new Phrase("header"));
headerCell.Colspan = 3;
headerCell.HorizontalAlignment = Element.ALIGN_CENTER;
table1.AddCell(headerCell);
//footer row
var footerCell = new PdfPCell(new Phrase("footer"));
footerCell.Colspan = 3;
footerCell.HorizontalAlignment = Element.ALIGN_CENTER;
table1.AddCell(footerCell);
//adding some rows
for (int i = 0; i < 70; i++)
{
//adds a new row
table1.AddCell(new Phrase("Cell[0], Row[" + i + "]"));
table1.AddCell(new Phrase("Cell[1], Row[" + i + "]"));
table1.AddCell(new Phrase("Cell[2], Row[" + i + "]"));
//sets the number of rows per page
if (i > 0 && table1.Rows.Count % 7 == 0)
{
pdfDoc.Add(table1);
table1.DeleteBodyRows();
pdfDoc.NewPage();
}
}
pdfDoc.Add(table1);
}
//open the final file with adobe reader for instance.
Process.Start("Test.pdf");
}
}
}
نکته جدید این مثال، قسمت زیر است:
if (i > 0 && table1.Rows.Count % 7 == 0)
{
pdfDoc.Add(table1);
table1.DeleteBodyRows();
pdfDoc.NewPage();
}
هر زمان که table1 به صفحه اضافه شود، header و footer هم اضافه خواهند شد، اما اگر BodyRows آن حذف نشود، دفعهی دومی که این table به صفحه اضافه میشود، شامل ردیفهای مثلا یک تا 10 خواهد بود بجای 6 تا 10 .
protobuf-net is a contract based serializer for .NET code, that happens to write data in the "protocol buffers" serialization format engineered by Google. The API, however, is very different to Google's, and follows typical .NET patterns (it is broadly comparable, in usage, to XmlSerializer, DataContractSerializer, etc). It should work for most .NET languages that write standard types and can use attributes.
Nuget Package : https://www.nuget.org/packages/protobuf-net
EF Code First #4
در برنامههای تحت وب، در بعضی موارد نیاز داریم تا برای کاربر، امکان ثبت دادههایش را با آپلود فایلهای Excel فراهم کنیم. برای مثال در مطلب خواندن اطلاعات از فایل اکسل با استفاده از LinqToExcel ، امکان خواندن از Excel توضیح داده شده، اما نقطه ضعف این روشها، وابستگی به Providerهای مایکروسافت است که در صورت عدم نصب آن ها:
Microsoft.Jet.OLEDB.4.0 provider --> Excel 97-2003 format (.xls) Microsoft.ACE.OLEDB.12.0 provider --> Excel 2007+ format (.xlsx)
The ‘Microsoft.Jet.OLEDB.4.0’ provider is not registered on the local machine The ‘Microsoft.ACE.OLEDB.12.0’ provider is not registered on the local machine
البته راه حل، نصب Office 2007 Data Connectivity Components یا Office 2010 Database Engine بر روی سرور میباشد. اما اگر هاست اشتراکی بوده و اجازه نصب نداشته باشیم؟
در این مقاله به بررسی کتابخانه ExcelDataReader میپردازیم که امکان خواندن فایلهای اکسل را بدون نیاز به نصب هرگونه پیش نیازی بر روی سرور، برای ما فراهم میکند.
برای این کار:
1- ابتدا یک پروژه خالی Asp.Net MVC را ایجاد میکنیم.
2- با استفاده از دستورات زیر در Package Manager Console بستههای ExcelDataReader و ExcelDataReader.DataSet را نصب میکنیم:
PM> Install-Package ExcelDataReader PM> Install-Package ExcelDataReader.DataSet
3- سپس کنترلر مورد نظر (در اینجا HomeController) را ایجاد نموده و اکشن Upload را بصورت زیر در آن قرار میدهیم:
public ActionResult Upload() { return View(); }
4- برای آپلود فایل، اکشن دیگری را با نام Upload نیاز داریم که آن را بصورت زیر ایجاد میکنیم:
توجه: در قطعه کد زیر سعی شده از حداقل کانفیگ کتابخانه استفاده شود. کانفیگ بیشتر
[HttpPost] [ValidateAntiForgeryToken] public ActionResult Upload(HttpPostedFileBase upload) { // اعتبار سنجی فایل آپلود شده if (upload != null && upload.ContentLength > 0 && (upload.FileName.EndsWith(".xls") || upload.FileName.EndsWith(".xlsx"))) { // با خواندن فایل به صورت باینری، این کتابخانه نیازی به نصب پیش نیازهای آفیس ندارد Stream stream = upload.InputStream; // نیازی به نگرانی در مورد پسوند فایل نیست // کتابخانه به صورت خودکار کلاس مورد نظر برای پسوند مربوطه را استفاده میکند // ExcelDataReader.ExcelBinaryReader یا ExcelDataReader.ExcelOpenXmlReader IExcelDataReader reader = ExcelReaderFactory.CreateReader(stream); // روش ذکر شده در قسمت دوم برای خواندن کل اطلاعات بصورت یکجا DataSet result = reader.AsDataSet(new ExcelDataSetConfiguration() { ConfigureDataTable = (tableReader) => new ExcelDataTableConfiguration() { // true: ردیف اول از فایل را به عنوان هدر در نظر میگیرد // مقدار پیش فرض: false UseHeaderRow = true } }); reader.Close(); return View(result.Tables[0]); } ModelState.AddModelError("File", "Please upload Excel file ..."); return View(); }
روش دیگر خواندن اطلاعات:
do { while (reader.Read()) { // reader.GetDouble(0); } } while (reader.NextResult());
5- خب حالا از یک View (با نام Upload) هم برای ارسال فایل و همچنین نمایش محتویات آپلود شده بصورت زیر استفاده میکنیم:
@model System.Data.DataTable @using System.Data; <h2>Upload File</h2> @using (Html.BeginForm("Upload", "Home", null, FormMethod.Post, new { enctype = "multipart/form-data" })) { @Html.AntiForgeryToken() @Html.ValidationSummary() <div> <input type="file" id="dataFile" name="upload" /> </div> <div> <input type="submit" value="Upload" /> </div> if (Model != null) { <table> <thead> <tr> @foreach (DataColumn col in Model.Columns) { <th>@col.ColumnName</th> } </tr> </thead> <tbody> @foreach (DataRow row in Model.Rows) { <tr> @foreach (DataColumn col in Model.Columns) { <td>@row[col.ColumnName]</td> } </tr> } </tbody> </table> } }
6- حالا پروژه را اجرا میکنیم تا خروجی را مشاهده کنیم.
ابتدا فایل مورد نظر را انتخاب و آپلود میکنیم:
و خروجی به صورت زیر خواهد بود:
کدهای کامل این مثال را از اینجا میتوانید دریافت کنید: UploadExcelFiles.rar