public void PageFinished(PdfWriter writer, Document document, IList<SummaryCellData> columnCellsSummaryData) { var pageSize = document.PageSize; var text = "صفحه " + writer.PageNumber + " از "; var textLen = _font.BaseFont.GetWidthPoint(text, _font.Size) + 17; var positionX = pageSize.Right - 10; var align = _direction == PdfRunDirection.RightToLeft ? Element.ALIGN_RIGHT : Element.ALIGN_LEFT; ColumnText.ShowTextAligned( canvas: _pdfContentByte, alignment: align, phrase: ReportMethod.SetFont(text, 20), x: positionX, y: pageSize.GetBottom(4), rotation: 0, runDirection: (int)_direction, arabicOptions: 0); var x = _direction == PdfRunDirection.RightToLeft ? positionX - textLen : positionX + textLen; _pdfContentByte.AddTemplate(_template, x, pageSize.GetBottom(4)); //-------------------------------------- if (_Info != null) { var table = new PdfGrid(1) { RunDirection = (int)_direction, WidthPercentage = 100 }; string[] msgField = { "مدیر گروه", _Info.Where(sp => sp.ID == _MemberID).FirstOrDefault().InstKindName, _Info.Where(sp => sp.ID == 0).FirstOrDefault().InstKindName, "امور مالی", "معاون پشتیبانی" }; string[] dataField = { "", _Info.Where(sp => sp.ID == _MemberID).FirstOrDefault().MasterName, _Info.Where(sp => sp.ID == 0).FirstOrDefault().MasterName, "", _Info.Where(sp => sp.ID == 1).FirstOrDefault().MasterName }; var infoTable = new PdfGrid(msgField.Length) { RunDirection = PdfWriter.RUN_DIRECTION_RTL, WidthPercentage = 100 }; foreach (var item in msgField) { infoTable.AddCell(ReportMethod.SetCell(item, PdfPCell.NO_BORDER, 1, PdfPCell.ALIGN_CENTER, PdfPCell.ALIGN_MIDDLE, true, 20)); } foreach (var item in dataField) { infoTable.AddCell(ReportMethod.SetCell(item, PdfPCell.NO_BORDER, 1, PdfPCell.ALIGN_CENTER, PdfPCell.ALIGN_MIDDLE, true, 20)); } table.AddCell(infoTable); table.SetTotalWidth(new[] { pageSize.Width - document.LeftMargin - document.RightMargin }); table.WriteSelectedRows( rowStart: 0, rowEnd: -1, xPos: document.LeftMargin, yPos: document.BottomMargin - 10, canvas: writer.DirectContent); } }
قبل ازاین مقاله، درباره راه اندازی و استفاده از کتابخانه Automapper بحث شده ولی موردی که شاید کمتر به آن توجه شده سرعت این نگاشت میباشد. در این مقاله با استفاده از نوشتن تست، این موضوع بررسی میشود.
کلاس ساده زیر را در نظر بگیرید که
برای مثال از سمت لایه دسترسی به داده گرفته شده است:
public enum PersonType { Real =0, Legal=1 } public class Person { public long PersonId { get; set; } public string Name { get; set; } public string Family { get; set; } public PersonType PersonType { get; set; } public Person(long personId, string name, string family, PersonType personType) { PersonId = personId; Name = name; Family = family; PersonType = personType; } }
از سازنده آن برای دریافت مقادیر مربوط به خصوصیات شیء استفاده شد.
در طرف دیگر نیز کلاسی برای نگاشت از آبجکت رسیده از سمت لایه داده ساخته میشود که برای نمایش در ویوها ایجاد شده است:
public class PersonDto { public long PersonId { get; set; } public string Name { get; set; } public string Family { get; set; } public PersonType PersonType { get; set; } public PersonDto(long personId, string name, string family, PersonType personType) { PersonId = personId; Name = name; Family = family; PersonType = personType; } }
همانطور که مشاهده میکنید در سازنده این کلاس نیز مقادیر خصوصیات، دریافت شدهاست.
برای ایجاد لیستی که در تست مورد استفاده قرار میگیرد نیز کلاس زیر را فراهم میکنیم:
public class PersonList { readonly List<Person> _list = new List<Person>(); public ReadOnlyCollection<Person> GetPersons() { if (!_list.Any()) { for (int i = 0; i < 100*1000; i++) { _list.Add(new Person(i + 1, "Person Name" + i, "Person Family" + i, (PersonType)(i % 2))); } } return _list.AsReadOnly(); } }
در اینجا برای
محسوس بودن نتیجه تست میتوان تعداد آبجکتهای لازم برای تست را تعیین کرد، فعلا 100
هزار آبجکت در نظر گرفته شده است:
for (int i = 0; i < 100*1000; i++) { _list.Add(new Person(i + 1, "Person Name" + i, "Person Family" + i, (PersonType)(i % 2))); }
برای
ارجاع به AutoMapper، با
استفاده از نیوگت، پکیج را به پروژه تست
ارجاع میدهیم: (در حال حاضر نسخه 5.1.1 استفاده شده است)
<package id="AutoMapper" version="5.1.1" targetFramework="net452" />
در سمت
تست نگاشت نیز از دو متد برای مقایسه استفاده میکنیم؛ یکی با استفاده از AutoMapper و دیگری بدون استفاده از آن:
[TestMethod] public void FillPersonDtoList_AutoMapperShouldMapPersonListToPersonDtoList_WhenLargeAmountOfPerson() { // arrange var personDtoList = new List<PersonDto>(); persons = new PersonList().GetPersons(); // act personDtoList = Mapper.Map<List<PersonDto>>(persons); //assert Assert.AreEqual(persons.Count, personDtoList.Count); } [TestMethod] public void FillPersonDtoList_UsingHandlyAssignment_WhenLargeAmountOfPerson() { // arrange var personDtoList = new List<PersonDto>(); persons = new PersonList().GetPersons(); // act foreach (var person in persons) { personDtoList.Add(new PersonDto(person.PersonId, person.Name, person.Family, person.PersonType)); } //assert Assert.AreEqual(persons.Count, personDtoList.Count); }
سرعت
نگاشت AutoMapper در نسخه حال حاضر تقریبا سه بار کندتر از استفاده معمول برای تهیه نگاشت
جدید از یک آبجکت است:
نکته: این تست با نسخه قدیمی تر(4.0.4.0) نیز انجام شده که این اختلاف سرعت نزدیک به 13 بار کندتر هم رسیده است.
پ.ن: سورس پروژه تست
مبانی TypeScript؛ ماژولها
testmd.ts:1Uncaught ReferenceError: exports is not defined require.js:143 Uncaught Error: Module name "testmd" has not been loaded yet for context: _. Use require([])
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>TypeScript HTML App</title> <link rel="stylesheet" href="app.css" type="text/css"/> <script src="Scripts/require.js"></script> <script src="Scripts/App/app.js"></script> </head> <body> <h1>TypeScript HTML App</h1> <div id="content"></div> </body> </html>
{ "compilerOptions": { "target": "es5", "outDir": "Scripts/App", "moduleResolution": "node", "module": "commonjs", "sourceMap": true, "experimentalDecorators": true, "emitDecoratorMetadata": true } }
مبانی TypeScript؛ ماژولها
Blazor.addEventListener('enhancedload', () => { // ... });
// @ts-ignore
{ "compilerOptions": { "strict": true, "removeComments": true, "sourceMap": false, "noEmitOnError": true, "forceConsistentCasingInFileNames": true, "skipLibCheck": true, "target": "ES2020", "outFile": "wwwroot/scripts/app.js" }, "include": [ "Scripts/**/*.ts" ], "exclude": [ "node_modules" ] }
SPList list = mWeb.GetList(strUrl); if (list != null) { for (int i = list.ItemCount - 1; i >= 0; i--) { list.Items[i].Delete(); } list.Update(); }
public static void DeleteAllItems(string site, string list) { using (SPSite spSite = new SPSite(site)) { using (SPWeb spWeb = spSite.OpenWeb()) { StringBuilder deletebuilder = BatchCommand(spWeb.Lists[list]); spSite.RootWeb.ProcessBatchData(deletebuilder.ToString()); } } } private static StringBuilder BatchCommand(SPList spList) { StringBuilder deletebuilder= new StringBuilder(); deletebuilder.Append("<?xml version=\"1.0\" encoding=\"UTF-8\"?><Batch>"); string command = "<Method><SetList Scope=\"Request\">" + spList.ID + "</SetList><SetVar Name=\"ID\">{0}</SetVar><SetVar Name=\"Cmd\">Delete</SetVar></Method>"; foreach (SPListItem item in spList.Items) { deletebuilder.Append(string.Format(command, item.ID.ToString())); } deletebuilder.Append("</Batch>"); return deletebuilder; }
// We prepare a String.Format with a String.Format, this is why we have a {{0}} string command = String.Format("<Method><SetList Scope=\"Request\">{0}</SetList><SetVar Name=\"ID\">{{0}}</SetVar><SetVar Name=\"Cmd\">Delete</SetVar><SetVar Name=\"owsfileref\">{{1}}</SetVar></Method>", list.ID); // We get everything but we limit the result to 100 rows SPQuery q = new SPQuery(); q.RowLimit = 100; // While there's something left while (list.ItemCount > 0) { // We get the results SPListItemCollection coll = list.GetItems(q); StringBuilder sbDelete = new StringBuilder(); sbDelete.Append("<?xml version=\"1.0\" encoding=\"UTF-8\"?><Batch>"); Guid[] ids = new Guid[coll.Count]; for (int i=0;i<coll.Count;i++) { SPListItem item = coll[i]; sbDelete.Append(string.Format(command, item.ID.ToString(), item.File.ServerRelativeUrl)); ids[i] = item.UniqueId; } sbDelete.Append("</Batch>"); // We execute it web.ProcessBatchData(sbDelete.ToString()); //We remove items from recyclebin web.RecycleBin.Delete(ids); list.Update(); } }
- ایجاد متغیرها به سادگی با شروع نوشتن نام متغیر با $ و بدون تعریف نوع آنها انجام میشود
- write-host حکم write را دارد و واضح است که نوشتن تنهای آن برای ایجاد یک line break میباشد.
- کامنت کردن با #
- عدم وجود semi colon برای اتمام فرامین
[System.Reflection.Assembly]::Load("Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c") [System.Reflection.Assembly]::Load("Microsoft.SharePoint.Portal, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c") [System.Reflection.Assembly]::Load("Microsoft.SharePoint.Publishing, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c") [System.Reflection.Assembly]::Load("System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a") write-host # Enter your configuration here $siteUrl = "http://mysharepointsite.example.com/" $listName = "Name of my list" $batchSize = 1000 write-host "Opening web at $siteUrl..." $site = new-object Microsoft.SharePoint.SPSite($siteUrl) $web = $site.OpenWeb() write-host "Web is: $($web.Title)" $list = $web.Lists[$listName]; write-host "List is: $($list.Title)" while ($list.ItemCount -gt 0) { write-host "Item count: $($list.ItemCount)" $batch = "<?xml version=`"1.0`" encoding=`"UTF-8`"?><Batch>" $i = 0 foreach ($item in $list.Items) { $i++ write-host "`rProcessing ID: $($item.ID) ($i of $batchSize)" -nonewline $batch += "<Method><SetList Scope=`"Request`">$($list.ID)</SetList><SetVar Name=`"ID`">$($item.ID)</SetVar><SetVar Name=`"Cmd`">Delete</SetVar><SetVar Name=`"owsfileref`">$($item.File.ServerRelativeUrl)</SetVar></Method>" if ($i -ge $batchSize) { break } } $batch += "</Batch>" write-host write-host "Sending batch..." # We execute it $result = $web.ProcessBatchData($batch) write-host "Emptying Recycle Bin..." # We remove items from recyclebin $web.RecycleBin.DeleteAll() write-host $list.Update() } write-host "Done."
کتابخانهی FastReflection
FastReflection.zip
// کار با یک لیست جنریک تو در تو var list = new List<User>(); for (int i = 0; i < 100; i++) { list.Add(new User { Id = i+1, Name = "name "+i, Address = new Address { Address1 = "Addr1- "+i, Address2 = "Addr2- "+i } }); } foreach (var item in list) { var propertyValues = new DumpNestedProperties().DumpPropertyValues(item, dumpLevel: 2); foreach (var result in propertyValues) { Console.WriteLine(result.PropertyName + " -> " + result.PropertyValue); } Console.WriteLine(); }
columns.AddColumn(column => { column.PropertyName("User"); column.CellsHorizontalAlignment(HorizontalAlignment.Center); column.IsVisible(true); column.Order(1); column.Width(3); column.HeaderCell("User"); column.CalculatedField(list => { var user = list.GetSafeStringValueOf("User"); var photo = new Uri(list.GetSafeStringValueOf("Photo")); var image = string.Format("<img src='{0}' />", photo); return @"<table style='width: 100%;'> <tr> <td>" + user + @"</td> </tr> <tr> <td>" + image + @"</td> </tr> </table> "; }); column.ColumnItemsTemplate(template => { template.Html(); // Using iTextSharp's limited HTML to PDF capabilities (HTMLWorker class). }); });
عموما محدود کردن دسترسی بر اساس IP بهتر است بر اساس راه حلهایی مانند فایروال، IPSec و یا RRAS IP Filter صورت گیرد که جزو بهینهترین و امنترین راه حلهای ممکن هستند.
در ادامه قصد داریم این محدودیت را با استفاده از امکانات خود اس کیوال سرور انجام دهیم (بلاک کردن کاربران بر اساس IP های غیرمجاز). مواردی که در ادامه ذکر خواهند شد در مورد اس کیوال سرور 2005 ، سرویس پک 2 به بعد و یا اس کیوال سرور 2008 صادق است.
اس کیوال سرور این قابلیت را دارد که میتوان بر روی کلیه لاگینهای صورت گرفته در سطح سرور تریگر تعریف کرد. به این صورت میتوان تمامی لاگینها را برای مثال لاگ کرد (جهت بررسی مسایل امنیتی) و یا میتوان هر لاگینی را که صلاح ندانستیم rollback نمائیم (ایجاد محدودیت روی لاگین در سطح سرور).
لاگ کردن کلیه لاگینهای صورت گرفته به سرور
ایجاد جدولی برای ذخیره سازی اطلاعات لاگینها:
USE [master]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[Logging](
[id] [int] IDENTITY(1,1) NOT NULL,
[LogonTime] [datetime] NULL,
[LoginName] [nvarchar](max) NULL,
[ClientHost] [varchar](50) NULL,
[LoginType] [varchar](100) NULL,
[AppName] [nvarchar](500) NULL,
[FullLog] [xml] NULL,
CONSTRAINT [PK_IP_Log] PRIMARY KEY CLUSTERED
(
[id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
ALTER TABLE [dbo].[Logging] ADD CONSTRAINT [DF_IP_Log_LogonTime] DEFAULT (getdate()) FOR [LogonTime]
GO
در ادامه یک تریگر لاگین را جهت ذخیره سازی اطلاعات کلیه لاگینها به سرور ایجاد مینمائیم:
USE [master]
GO
CREATE TRIGGER LogonTrigger
ON ALL SERVER
FOR LOGON
AS
BEGIN
DECLARE @data XML
SET @data = EVENTDATA()
INSERT INTO [Logging]
(
[LoginName],
[ClientHost],
[LoginType],
[AppName],
[FullLog]
)
VALUES
(
@data.value('(/EVENT_INSTANCE/LoginName)[1]', 'nvarchar(max)'),
@data.value('(/EVENT_INSTANCE/ClientHost)[1]', 'varchar(50)'),
@data.value('(/EVENT_INSTANCE/LoginType)[1]', 'varchar(100)'),
APP_NAME(),
@data
)
END
SELECT TOP 100 * FROM [master].[dbo].[Logging] ORDER BY id desc
محدود کردن کاربران بر اساس IP
ClientHost ایی که در رخداد لاگین فوق بازگشت داده میشود همان IP کاربر راه دور است. برای فیلتر کردن IP های غیرمجاز، ابتدا در دیتابیس مستر یک جدول برای ذخیره سازی IP های مجاز ایجاد میکنیم و IP های کلیه کلاینتهای معتبر خود را در آن وارد میکنیم:
USE [master]
GO
CREATE TABLE [IP_RESTRICTION](
[ValidIP] [varchar](15) NOT NULL,
CONSTRAINT [PK_IP_RESTRICTION] PRIMARY KEY CLUSTERED
(
[ValidIP] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
سپس تریگر لاگین ما برای منع کاربران غیرمجاز بر اساس IP ، به صورت زیر خواهد بود:
USE [master]
GO
CREATE TRIGGER [LOGIN_IP_RESTRICTION]
ON ALL SERVER
FOR LOGON
AS
BEGIN
DECLARE @host NVARCHAR(255);
SET @host = EVENTDATA().value('(/EVENT_INSTANCE/ClientHost)[1]', 'nvarchar(max)');
IF (
NOT EXISTS(
SELECT *
FROM MASTER.dbo.IP_RESTRICTION
WHERE ValidIP = @host
)
)
BEGIN
ROLLBACK;
END
END;
تریگر فوق خطرناک است! ممکن است خودتان هم دیگر نتوانید لاگین کنید!! (حتی با اکانت ادمین)
بنابراین قبل از لاگین حتما IP لوکال و یا ClientHost لوکال را هم وارد کنید.
اگر گیر افتادید به صورت زیر میشود رفع مشکل کرد:
تنها حالتی که تریگر لاگین را فعال نمیکند Dedicated Administrator Connection است یا DAC هم به آن گفته میشود. به صورت پیش فرض برای ایجاد این اتصال اختصاصی باید به کامپیوتری که اس کیوال سرور بر روی آن نصب است به صورت لوکال لاگین کرد و سپس در خط فرمان دستور زیر را صادر کنید (حرف A آن باید بزرگ باشد):
C:\>sqlcmd -A -d master -q "insert into IP_RESTRICTION(validip) values('<local machine>')"
این نوع تریگرها در قسمت server objects در management studio ظاهر میشوند.