نظرات مطالب
Defensive Programming - بازگشت نتایج قابل پیش بینی توسط متدها
باید عرض کنم بله وقتی کاربری یافت نشد چه کاری می‌توانیم انجام دهیم؟ 
مطلب پایه‌ای در مورد صدور استثناءها در اینجا هست: Dont Use Exceptions For Flow Control و قسمتی از آن به این صورت است:
 Errors should be handled via exceptions, but successes shouldn't
 مشکلی که در اینجا هست این است که بازگشت null یک کاربر در یک کوئری، یک error نیست؛ یک بازگشت و عملیات موفقیت آمیز است. به همین جهت صدور استثناء برای دریافت آن نباید رخ دهد:  
The typical meaning of the word "exception" is an event that is unexpected rather than part of normal operation; otherwise people would just say "event" 
و اینکه نتیجه‌ی یک متد باید در همان متد به صورت استثناء درنظر گرفته شود، غیرضروری است و این تصمیم گیری باید به عهده‌ی فراخوان گذاشته شود. گاهی می‌خواهیم بررسی کنیم کاربری در دیتابیس هست؟ اگر بله، آ‌‌ن‌را تکراری ثبت نکنیم. گاهی می‌خواهیم بررسی کنیم اگر در دیتابیس هست، اطلاعات او را به روز رسانی کنیم. اینکه کوئری مدنظر نال بر می‌گرداند، تصمیم گیری در مورد اهمیت آن ربطی به آن متد ندارد و صرفا به عهده‌ی فراخوان است:
Methods should have a way to indicate various kinds of results. Whether those results are treated as exceptional should depend on the caller.  
مطالب
استفاده از SQLDom برای آنالیز عبارات T-SQL
به همراه بسته Features pack اس کیوال سرور 2012، دو بسته SqlDom.msi نیز وجود دارند (نسخه‌های X86 و X64). این بسته حاوی اسمبلی Microsoft.SqlServer.TransactSql.ScriptDom.dll می‌باشد که نهایتا در آدرس Program Files\Microsoft SQL Server\110\SDK\Assemblies کپی خواهد شد.
به کمک آن می‌توان عبارات پیچیده T-SQL را Parse و آنالیز کرد. البته باید در نظر داشت هرچند این بسته جهت SQL Server 2012 ارائه شده اما این اسمبلی با نگارش‌های 2005 به بعد اس کیوال سرور کاملا سازگار است و اساسا نیازی هم به SQL Server ندارد. در ادامه مروری خواهیم داشت بر نحوه استفاده از آن.


یافتن کوئری‌های * Select در بین انبوهی از اسکریپت‌ها به کمک SQLDom

در مورد مضرات کوئری‌های * select پیشتر مطلبی را در این سایت خوانده‌اید. در ادامه قصد داریم به کمک امکانات اسمبلی Microsoft.SqlServer.TransactSql.ScriptDom.dll، تعدادی عبارت T-SQL را آنالیز کرده و مشخص کنیم که آیا حاوی * select هستند یا خیر. کد کامل آن‌را در ذیل مشاهده می‌کنید:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Microsoft.SqlServer.TransactSql.ScriptDom;

namespace DbCop
{
    // Microsoft® SQL Server® 2012 Transact-SQL ScriptDom 
    // SQL Server 2012 managed parser, Supports SQL Server 2005+
    // SQLDom.msi (redist x86/x64)
    // http://www.microsoft.com/en-us/download/details.aspx?id=29065
    // X86: http://go.microsoft.com/fwlink/?LinkID=239634&clcid=0x409
    // X64: http://go.microsoft.com/fwlink/?LinkID=239635&clcid=0x409
    // Program Files\Microsoft SQL Server\110\SDK\Assemblies\Microsoft.SqlServer.TransactSql.ScriptDom.dll

    class Program
    {
        static void Main()
        {
            const string tSql = @"
                -- select * in PROCEDURE
                CREATE PROCEDURE dbo.SelectStarTest
                AS
                SELECT * FROM dbo.tbl1
                go

                -- select * in PROCEDURE with TableVar
                Create PRocedure SelectAll
                AS
                Declare @X table(Id integer)
                Select * from @x
                go

                -- select * in PROCEDURE with ctex
                CREATE PROCEDURE dbo.SelectAllCte
                AS 
                WITH ctex
                AS (
                SELECT * FROM sys.objects
                )
                SELECT * FROM ctex
                go

                -- normal select *
                select * from tbl1; 
                select * from dbo.tbl2;
            ";

            IList<ParseError> errors;
            TSqlScript sqlFragment;
            using (var reader = new StringReader(tSql))
            {
                var parser = new TSql110Parser(initialQuotedIdentifiers: true);
                sqlFragment = (TSqlScript)parser.Parse(reader, out errors);
            }

            if (errors != null && errors.Any())
            {
                var sb = new StringBuilder();
                foreach (var error in errors)
                    sb.AppendLine(error.Message);

                throw new InvalidOperationException(sb.ToString());
            }

            var i = 0;
            foreach (var batch in sqlFragment.Batches)
            {
                Console.WriteLine("Batch: {0}, Statement(s): {1}", ++i, batch.Statements.Count);
                foreach (var statement in batch.Statements)
                {
                    processStatement(statement);
                }
                Console.WriteLine();
            }

            Console.WriteLine("\nPress a key...");
            Console.Read();
        }

        private static void processStatement(TSqlStatement statement)
        {
            var createProcedureStatement = statement as CreateProcedureStatement;
            if (createProcedureStatement != null)
            {
                var statementList = createProcedureStatement.StatementList;
                foreach (var procedureStatement in statementList.Statements)
                {
                    processStatement(procedureStatement);
                }
            }

            var selectStatement = statement as SelectStatement;
            if (selectStatement != null)
            {
                var query = selectStatement.QueryExpression;
                var selectElements = ((QuerySpecification)query).SelectElements;
                foreach (var selectElement in selectElements)
                {
                    var expression = selectElement as SelectStarExpression;
                    if (expression == null) continue;
                    Console.WriteLine(
                        "`Select *` detected @StartOffset:{0}, Line:{1}, T-SQL: {2}",
                        expression.StartOffset,
                        expression.StartLine,
                        statementToString(selectStatement));
                }
            }
        }

        private static string statementToString(TSqlFragment selectStatement)
        {
            var text = new StringBuilder();
            for (var i = selectStatement.FirstTokenIndex; i <= selectStatement.LastTokenIndex; i++)
            {
                text.Append(selectStatement.ScriptTokenStream[i].Text);
            }
            return text.ToString();
        }
    }
}

توضیحات:
پس از نصب SQLDom.msi، ارجاعی را به اسمبلی زیر اضافه نمائید تا بتوانید کد فوق را کامپایل کنید:
Program Files\Microsoft SQL Server\110\SDK\Assemblies\Microsoft.SqlServer.TransactSql.ScriptDom.dll

کار با ایجاد وهله‌ای از TSql110Parser شروع می‌شود. متد Parse آن، آرگومانی از نوع TextReader را قبول می‌کند. برای مثال با استفاده از StringReader می‌توان محتوای یک متغیر رشته‌ای را به آن ارسال کرد و یا توسط StreamReader یک فایل sql را.
پس از فراخوانی متد Parse، بهتر است بررسی شود که آیا عبارت T-SQL دریافتی معتبر بوده است یا خیر. اینکار را توسط لیستی از ParseError‌های دریافتی می‌توان انجام داد.
خروجی متد Parse، حاوی یک سری Batch آنالیز شده است. هر عبارت Go در اینجا یک Batch را تشکیل می‌دهد. سپس در داخل هر batch به دنبال batch.Statements خواهیم گشت تا بتوان به عبارات T-SQL آن‌ها دسترسی یافت.
در ادامه کار اصلی توسط متد processStatement صورت می‌گیرد. عبارات دریافتی، در حالت کلی از نوع TSqlStatement هستند اما در اصل می‌توانند یکی از مشتقات آن نیز باشند. در اینجا فقط دو مورد CreateProcedureStatement و SelectStatement بررسی شده‌اند (مطابق رشته tSql ابتدای مثال). هر دو عبارت، از کلاس TSqlStatement مشتق شده‌اند.
در متد processStatement عبارات select معمولی و همچنین آن‌هایی که داخل رویه‌های ذخیره شده تعریف شده‌اند، استخراج شده و در نهایت بررسی می‌شوند که آیا از نوع SelectStarExpression هستند یا خیر (همان * select صورت مساله).
خروجی مثال فوق به شرح زیر است:
Batch: 1, Statement(s): 1
`Select *` detected @StartOffset:140, Line:5, T-SQL: SELECT * FROM dbo.tbl1

Batch: 2, Statement(s): 1
`Select *` detected @StartOffset:368, Line:12, T-SQL: Select * from @x

Batch: 3, Statement(s): 1
`Select *` detected @StartOffset:659, Line:22, T-SQL: WITH ctex
                AS (
                SELECT * FROM sys.objects
                )
                SELECT * FROM ctex

Batch: 4, Statement(s): 2
`Select *` detected @StartOffset:753, Line:26, T-SQL: select * from tbl1;
`Select *` detected @StartOffset:791, Line:27, T-SQL: select * from dbo.tbl2;
 
مطالب
بررسی کارآیی کوئری‌ها در SQL Server - قسمت سوم - جمع آوری اطلاعات آماری کوئری‌ها توسط DMO's
Extended events ای که در قسمت قبل بررسی شدند، جهت جمع آوری اطلاعات آماری تک کوئری‌ها مورد استفاده قرار می‌گیرند؛ اما Dynamic management objects یا به اختصار DMO's، تجمعی عمل می‌کنند (برای مثال جهت محاسبه‌ی میانگین logical reads چند کوئری مانند هم). متن یک کوئری و پلن آن، توسط DMO's مختلفی قابل استخراج هستند. متن یک کوئری توسط sys.dm_exec_sql_text قابل استخراج است و برای دسترسی به کوئری پلن‌ها از sys.dm_exec_query_plan، sys.dm_exec_cached_plans و sys.dm_exec_text_query_plan استفاده می‌شود. در این حالت برای دسترسی به اطلاعات آماری از sys.dm_exec_query_stats و sys.dm_exec_function_stats کمک گرفته خواهد شد.


استفاده از Dynamic management objects برای جمع آوری اطلاعات آماری کوئری‌ها

در ادامه در طی چند مثال، روش استخراج اطلاعات آماری کوئری‌ها را توسط DMO's بررسی می‌کنیم.

دریافت متن کوئری‌های در حال اجرا

توسط کوئری زیر که توسط تابع sys.dm_exec_sql_text اجرا می‌شود، می‌توان لیست کوئری‌های در حال اجرای بر روی بانک‌های اطلاعاتی جاری را بدست آورد:
SELECT
    [r].[session_id],
    DB_NAME([r].[database_id]) [DatabaseName],
    [t].[text]
FROM sys.dm_exec_requests [r]
CROSS APPLY sys.dm_exec_sql_text([r].sql_handle) [t];
GO
در اینجا text، همان متن کوئری است و هربار که این کوئری اجرا می‌شود، نتیجه‌ی متفاوتی را بر اساس کوئری‌هایی که در آن لحظه در حال اجرا هستند، دریافت خواهیم کرد.
تابع sys.dm_exec_sql_text برای اجرا نیاز به یک sql_handle دارد که آن‌را از طریق sys.dm_exec_requests می‌توان تامین کرد.


دریافت پلن کوئری‌های در حال اجرا

توسط کوئری زیر که توسط تابع sys.dm_exec_query_plan اجرا می‌شود، می‌توان لیست پلن کوئری‌های در حال اجرای بر روی بانک‌های اطلاعاتی جاری را بدست آورد:
SELECT
    [r].[session_id],
    DB_NAME([r].[database_id]) [DatabaseName],
    [t].[text],
    [p].[query_plan]
FROM sys.dm_exec_requests [r]
CROSS APPLY sys.dm_exec_sql_text([r].sql_handle) [t]
CROSS APPLY sys.dm_exec_query_plan([r].[plan_handle]) [p];
GO
تابع sys.dm_exec_query_plan برای اجرا نیاز به یک plan_handle دارد که آن‌را از طریق sys.dm_exec_requests می‌توان تامین کرد.
حاصل این کوئری، به همراه text یا اصل متن کوئری‌های در حال اجرا و همچنین query_plan، یا همان اطلاعات XML ای پلن که در قسمت اول، نمونه‌ای از آن‌را بررسی کردیم، می‌باشد که با کلیک بر روی هر کدام در management studio، نمایش گرافیکی آن‌ها ظاهر خواهد شد. البته این پلن‌ها، تنها تخمین‌ها را به همراه دارند؛ چون از کش خوانده می‌شوند.


دریافت لیست پلن‌های کش شده

توسط Viewای به نام sys.dm_exec_cached_plans می‌توان به لیست پلن‌های کش شده‌ی در سیستم دسترسی یافت:
SELECT *
FROM sys.dm_exec_cached_plans;
البته خروجی آن، آنچنان جالب نیست. چون یکی از ستون‌های آن، فقط حاوی همان plan_handle ای است که در مثال قبل بررسی کردیم و به خودی خود، حاوی اطلاعات قابل مشاهده‌ای نیست. به همین جهت اگر بخواهیم آن‌را با کوئری‌هایی که تاکنون نوشتیم، ترکیب کنیم به کوئری زیر خواهیم رسید:
SELECT
    [r].[session_id],
    DB_NAME([r].[database_id]) [DatabaseName],
    [cp].[objtype],
    [cp].[size_in_bytes],
    [t].[text],
    [p].[query_plan]
FROM sys.dm_exec_requests [r]
CROSS APPLY sys.dm_exec_sql_text([r].sql_handle) [t]
CROSS APPLY sys.dm_exec_query_plan([r].[plan_handle]) [p]
    JOIN sys.dm_exec_cached_plans [cp]
    ON [r].[plan_handle] = [cp].[plan_handle];
GO
مزیت این کوئری نسبت به موارد قبلی، وجود ستون‌های جدید objtype و size_in_bytes است که بیانگر نوع کوئری، مانند AdHoc و اندازه‌ی پلن در کش هستند.


دریافت متن پلن‌های تو در تو و عمیق

با استفاده از تابع sys.dm_exec_text_query_plan می‌توان به متن پلن‌های عمیق دسترسی یافت. در این حالت خروجی کوئری در management studio به صورت یک لینک قابل کلیک ظاهر نمی‌شود و صرفا یک متن قابل کپی است که می‌توان آن‌را با پسوند sqlplan برای بررسی‌های بعدی، ذخیره کرد:
SELECT
    [r].[session_id],
    DB_NAME([r].[database_id]) [DatabaseName],
    [tq].[query_plan]
FROM sys.dm_exec_requests [r]
CROSS APPLY sys.dm_exec_text_query_plan([r].plan_handle, 0, -1) [tq];
GO
در اینجا اعداد 0 و 1- به معنای ابتدا و انتهای batch هستند.


دریافت اطلاعات آماری کوئری‌های درحال اجرا

توسط viewای به نام sys.dm_exec_query_stats می‌توان به اطلاعات آماری کوئری‌های در حال اجرا دسترسی یافت:
SELECT *
FROM sys.dm_exec_query_stats;
GO
این کوئری تعداد ستون‌های قابل توجهی را به همراه دارد مانند Physical reads، logical reads و .... به همین جهت نیاز است اطلاعات مفید آن‌را فیلتر کرد:
SELECT
    [qs].[last_execution_time],
    [qs].[execution_count],
    [qs].[total_logical_reads]/[qs].[execution_count] [AvgLogicalReads],
    [qs].[max_logical_reads],
    [t].[text],
    [p].[query_plan]
FROM sys.dm_exec_query_stats [qs]
CROSS APPLY sys.dm_exec_sql_text([qs].sql_handle) [t]
CROSS APPLY sys.dm_exec_query_plan([qs].[plan_handle]) [p]
WHERE [qs].[execution_count] > 25
    OR [qs].[total_logical_reads] > 10000
ORDER BY [qs].[total_logical_reads]/[qs].[execution_count] DESC;
GO
این کوئری در حقیقت ترکیبی است از کوئری‌هایی که تاکنون نوشتیم و در آن text و query_plan از sys.dm_exec_sql_text و sys.dm_exec_query_plan تامین شده‌اند، به همراه تعدادی ستون مفید sys.dm_exec_query_stats مانند last_execution_time و AvgLogicalReads. به علاوه در اینجا کوئری‌هایی که بیشتر از 25 بار اجرا شده‌اند و یا total_logical_reads آن‌ها بیش از 10 هزار بوده، در خروجی ظاهر خواهند شد (مفهوم تجمعی بودن DMO's).

از SQL Server 2016 به بعد، امکان دریافت اطلاعات آماری توابع نیز میسر شده‌است:
SELECT *
FROM sys.dm_exec_function_stats;
GO

یک نکته: قابلیت جدیدی تحت عنوان Query Store از زمان SQL Server 2016 معرفی شد‌ه‌است و کار آن دریافت تمام اطلاعاتی است که تاکنون بررسی کردیم و تفاوت آن، در ذخیره شده بودن آن است. یعنی این اطلاعات را داخل بانک اطلاعاتی در حال بررسی ذخیره می‌کند که شامل متن و پلن کوئری و همچنین اطلاعات آماری آن است که توسط DMO's تهیه می‌شود.
مطالب
نکات امنیتی OWASP مخصوص برنامه نویس‌های دات نت
OWSAP ارگانی است غیرانتفاعی که هدف آن ترویج طراحی برنامه‌های امن وب است. در این راه هم مطالب آموزشی بسیار ارزشمندی را منتشر کرده است [+]. در لینک‌های زیر این مطالب از دیدگاه برنامه نویس‌های دات نت مورد بررسی قرار گرفته‌اند. هر چند مطابق آخرین گزارش WhiteHat که اخیرا منتشر شده [+]، تعداد Exploits مربوط به ASP.NET در مقایسه با PHP و جاوا بسیار کمتر بوده اما نیاز است تا با مشکلات عمومی موجود و راه‌حل‌های مرتبط بیشتر آشنا شد:



مطالب
گزارشگیری از تاریخچه‌ی اجرای کوئری‌ها در SQL Server

چند روز قبل مشکلی رخ داده بود به این شرح!
سروری کهSQL server بر روی آن نصب بود بخاطر SQL server ، بیش از 50 درصد CPU usage مداوم پیدا کرده بود. عموما مصرف CPU اس کیوال سرور روی سرورهای قوی بالا نیست و تداوم این حالت به این شدت یعنی بروز مشکل.
اینجا است که این سؤال پیش میاد، SQL Server الان داره چکار میکنه که تا این حد به صورت مداوم مصرف CPU آن بالا رفته؟ (حدودا 2 ساعت تمام به صورت مداوم مصرف CPU بالای 50 درصد بود)
با استفاده از کوئری زیر می‌شود، عبارات SQL ایی را که هم اکنون در حال اجرا هستند را به صورت زنده مشاهده کرد: (در اس کیوال سرور 2005 به بعد)
USE master;
SELECT st.text, r.session_id, r.status, r.command, r.cpu_time, r.total_elapsed_time
FROM sys.dm_exec_requests r
CROSS APPLY sys.dm_exec_sql_text(sql_handle) AS st

مشکل از بروز یک loop بود که به این صورت دقیقا کوئری مربوطه تشخیص داده شد و برطرف شد.

همچنین با استفاده از کوئری زیر می‌توان آخرین 50 کوئری اجرا شده در SQL server را به همراه زمان اجرای آنها گزارشگیری کرد:
SELECT TOP 50
deqs.last_execution_time AS [Time],
dest.text AS [Query]
FROM sys.dm_exec_query_stats AS deqs
CROSS apply
sys.dm_exec_sql_text(deqs.sql_handle) AS dest
ORDER BY
deqs.last_execution_time DESC

اگر علاقمند باشید در مورد این نوع کوئری‌ها اطلاعات بیشتری کسب کنید، مطالعه مقاله زیر توصیه می‌شود:
http://www.sqlteam.com/article/dynamic-management-views

اشتراک‌ها
سری 8 قسمتی NET MAUI. برای تازه‌کارها

.NET MAUI for Beginners
8 videos
.NET Multi-platform App UI (.NET MAUI) is a framework for building modern, multi-platform, natively compiled iOS, Android, macOS, and Windows apps using C# and XAML in a single codebase. In this video series you will learn how to get started with .NET MAUI, C#, and Visual Studio to build your very first cross-platform desktop and mobile app. 

سری 8 قسمتی NET MAUI. برای تازه‌کارها
اشتراک‌ها
آشنایی با FluentValidation

Webinar agenda:

0:00 – Introduction
0:55 – OSS in the .NET Community
3:00 – Welcome, Jeremy and FluentValidation
6:17 – OSS Power-Ups
8:11 – Rider plugin for FluentValidation
9:07 – Introduction to FluentValidation
13:36 – Custom validation messages and placeholders
18:33 – Cross-property validation
21:17 – Nested and compositional validation
25:57 – Validator extensions
31:40 – Unit testing validators
51:25 – ASP.NET Core integration
1:03:35 – Synchronous and asynchronous validation
1:07:33 – Q&A and wrap-up
 

آشنایی با FluentValidation
اشتراک‌ها
croc ابزاری است که به هر دو کامپیوتری امکان انتقال ساده و ایمن فایل‌ها و فولدرها را می دهد.

croc is a tool that allows any two computers to simply and securely transfer files and folders. AFAIK, croc is the only CLI file-transfer tool that does all of the following:

  • allows any two computers to transfer data (using a relay)
  • provides end-to-end encryption (using PAKE)
  • enables easy cross-platform transfers (Windows, Linux, Mac)
  • allows multiple file transfers
  • allows resuming transfers that are interrupted
  • local server or port-forwarding not needed
  • ipv6-first with ipv4 fallback 
croc ابزاری است که به هر دو کامپیوتری امکان انتقال ساده و ایمن فایل‌ها و فولدرها را می دهد.
اشتراک‌ها
Git for Windows 2.28.0 منتشر شد

New Features

Git for Windows 2.28.0 منتشر شد
اشتراک‌ها
دوره مقدماتی ASP.NET Core از Microsoft Virtual Academy

Explore ASP.NET Core SDK and tooling, look at .NET Core CLI, and learn how to build an ASP.NET Core app with Razor Pages MVC. Plus, get the details on logging and diagnostics. Find lots of cross-platform goodness and get .NET ready, as you learn more about this framework for building modern cloud-based web apps. Build your first ASP.NET project, and gear up for the intermediate ASP.NET Core course. 

دوره مقدماتی ASP.NET Core از Microsoft Virtual Academy