نظرات مطالب
مروری بر قابلیت جدید ASP.NET FriendlyUrls
یکی از اعضای تیم ASP.NET در اینجا عنوان کرده که Medium Trust به صورت رسمی منسوخ شده درنظر گرفته می‌شود و در محصولات آتی ما به هیچ عنوان مدنظر نخواهد بود و هاست‌ها باید به روش OS-level isolation کوچ کنند.
مطالب
بهبود کارآیی حلقه‌های foreach در دات نت 7
بالاخره تفاوت کارآیی بین حلقه‌های for و foreach در دات نت 7 برطرف شده‌است که این مورد نیز یکی دیگر از دلایل بهبود کارآیی LINQ در دات نت 7 است. در این مطلب به همراه آزمایشی، این مورد را بررسی خواهیم کرد.


تدارک یک آزمایش برای بررسی کارآیی حلقه‌های for و foreach در دات نت 7

یک برنامه‌ی کنسول جدید را ایجاد کرده و سپس کتابخانه‌ی BenchmarkDotNet را با TargetFramework دات نت 7 به صورت زیر به پروژه اضافه می‌کنیم:
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net7.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="BenchmarkDotNet" Version="0.13.4" />
  </ItemGroup>
</Project>
در ادامه به این پروژه، کلاس زیر را اضافه می‌کنیم:
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;

namespace NET7Loops;

[SimpleJob(RuntimeMoniker.Net60)]
[SimpleJob(RuntimeMoniker.Net70)]
[MemoryDiagnoser(false)]
public class Benchmarks
{
    private int[] ItemsArray;
    private List<int> ItemsList;

    [GlobalSetup]
    public void Setup()
    {
        var random = new Random(420);
        var randomItems = Enumerable.Range(0, 1000).Select(_ => random.Next());
        ItemsArray = randomItems.ToArray();
        ItemsList = randomItems.ToList();
    }

    [Benchmark]
    public void For_Array()
    {
        for (var i = 0; i < ItemsArray.Length; i++)
        {
            var item = ItemsArray[i];
        }
    }

    [Benchmark]
    public void For_List()
    {
        for (var i = 0; i < ItemsList.Count; i++)
        {
            var item = ItemsList[i];
        }
    }

    [Benchmark]
    public void ForEach_Array()
    {
        foreach (var item in ItemsArray)
        {
        }
    }

    [Benchmark]
    public void ForEach_List()
    {
        foreach (var item in ItemsList)
        {
        }
    }
}
که توسط دستورات زیر در حالت release اجرا شده و نتایج نهایی را نمایش می‌دهد:
using BenchmarkDotNet.Running;
using NET7Loops;

BenchmarkRunner.Run<Benchmarks>();
توضیحات:

- می‌توان یک پروژه را یکبار بر اساس دات نت 7 و یکبار هم بر اساس دات نت 6 با تغییر target framework آن‌ها کامپایل و اجرا کرد تا بتوان نتایج این دو را با هم مقایسه کرد و یا می‌توان با ذکر [SimpleJob(RuntimeMoniker.Net60)] و همچنین [SimpleJob(RuntimeMoniker.Net70)]، این مورد را به صورت خودکار به BenchmarkDotNet دات نت واگذار کرد.
- در این آزمایش، ابتدا یک آرایه و یک لیست را تهیه می‌کنیم.
- سپس یکبار حلقه‌های for و foreach را بر روی آرایه و همین عملیات را بر روی لیست تهیه شده، تکرار می‌کنیم.

نتایج حاصل به صورت زیر هستند:


همانطور که در نتایج فوق هم مشاهده می‌کنید:
در دات نت 6
- تفاوتی بین کارآیی حلقه‌ها‌ی for و foreach، زمانیکه بر روی یک آرایه اجرا می‌شوند، وجود ندارد.
- اما کارآیی حلقه‌ی foreach نسبت به حلقه‌ی for، زمانیکه بر روی یک لیست اجرا می‌شوند، تقریبا 50 درصد کمتر است.

در دات نت 7
- تفاوتی بین کارآیی حلقه‌ها‌ی for و forach، زمانیکه بر روی یک آرایه اجرا می‌شوند، وجود ندارد. بنابراین از این لحاظ با دات نت 6 تفاوتی ندارد.
- اما کارآیی حلقه‌ی foreach نسبت به حلقه‌ی for، زمانیکه بر روی یک لیست اجرا می‌شود، تقریبا یکسان و قابل چشم‌پوشی است. یعنی در دات نت 7، کارآیی این دو حلقه یکی شده‌است. اما چرا؟


روشی در جهت یافتن یکی بودن سرعت حلقه‌های for و foreach بر اساس خروجی کامپایلر

با مشاهده‌ی نتایج حاصل از BenchmarkDotNet می‌توان به بهبود کارآیی حاصل پی‌برد؛ اما برای مثال چرا زمانیکه از آرایه استفاده می‌شود، حتی در دات نت 6، تفاوتی بین دو حلقه‌ی for و foreach وجود ندارد، اما زمانیکه از لیست‌ها استفاده می‌شود، این کارآیی 50 درصد افت می‌کند؟
برای پاسخ به این سؤال می‌توان از IL Viewer موجود در Rider استفاده کرد که آخرین نگارش آن به همراه نمایش #Low-level C هم هست:

این همان خروجی است که توسط کامپایلر، پیش از تولید کدهای باینری نهایی، تهیه می‌شود. یعنی اگر قصد داشته باشیم تا درک کامپایلر را نسبت به قطعه کدی مشاهده کنیم، می‌توان به این خروجی مراجعه کرد که به صورت زیر است:
// Decompiled with JetBrains decompiler
// Type: NET7Loops.Benchmarks
// Assembly: NET7Loops, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
// MVID: E398BEE7-8123-4C55-AF9A-F7D83DDA73F1
// Assembly location: C:\Prog\1401\Net7Tests\NET7Loops\bin\Debug\net7.0\NET7Loops.dll
// Compiler-generated code is shown

using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;

namespace NET7Loops
{
  [NullableContext(1)]
  [Nullable(0)]
  [SimpleJob(RuntimeMoniker.Net60, -1, -1, -1, -1, null, false)]
  [SimpleJob(RuntimeMoniker.Net70, -1, -1, -1, -1, null, false)]
  [MemoryDiagnoser(false)]
  public class Benchmarks
  {
    private int[] ItemsArray;
    private List<int> ItemsList;

    [GlobalSetup]
    public void Setup()
    {
      Benchmarks.<>c__DisplayClass2_0 cDisplayClass20 = new Benchmarks.<>c__DisplayClass2_0();
      cDisplayClass20.random = new Random(420);
      IEnumerable<int> source = Enumerable.Range(0, 1000).Select<int, int>(new Func<int, int>((object) cDisplayClass20, __methodptr(<Setup>b__0)));
      this.ItemsArray = source.ToArray<int>();
      this.ItemsList = source.ToList<int>();
    }

    [Benchmark(23, "C:\\Prog\\1401\\Net7Tests\\NET7Loops\\Benchmarks.cs")]
    public void For_Array()
    {
      for (int index = 0; index < this.ItemsArray.Length; ++index)
      {
        int items = this.ItemsArray[index];
      }
    }

    [Benchmark(32, "C:\\Prog\\1401\\Net7Tests\\NET7Loops\\Benchmarks.cs")]
    public void For_List()
    {
      for (int index = 0; index < this.ItemsList.Count; ++index)
      {
        int items = this.ItemsList[index];
      }
    }

    [Benchmark(41, "C:\\Prog\\1401\\Net7Tests\\NET7Loops\\Benchmarks.cs")]
    public void ForEach_Array()
    {
      int[] itemsArray = this.ItemsArray;
      for (int index = 0; index < itemsArray.Length; ++index)
      {
        int num = itemsArray[index];
      }
    }

    [Benchmark(49, "C:\\Prog\\1401\\Net7Tests\\NET7Loops\\Benchmarks.cs")]
    public void ForEach_List()
    {
      List<int>.Enumerator enumerator = this.ItemsList.GetEnumerator();
      try
      {
        while (enumerator.MoveNext())
        {
          int current = enumerator.Current;
        }
      }
      finally
      {
        enumerator.Dispose();
      }
    }

    public Benchmarks()
    {
      base..ctor();
    }

    [CompilerGenerated]
    private sealed class <>c__DisplayClass2_0
    {
      [Nullable(0)]
      public Random random;

      public <>c__DisplayClass2_0()
      {
        base..ctor();
      }

      internal int <Setup>b__0(int _)
      {
        return this.random.Next();
      }
    }
  }
}
در این خروجی بهتر می‌توان مشاهده کرد که چرا در حالت استفاده‌ی از آرایه‌ها، تفاوتی بین حلقه‌های for و foreach نیست؛ چون هر دو به صورت حلقه‌ی for تفسیر می‌شوند:
for (int index = 0; index < this.ItemsArray.Length; ++index)
{
   int items = this.ItemsArray[index];
}
اما زمانیکه به لیست‌ها می‌رسیم، حلقه‌ی foreach به صورت زیر تفسیر می‌شود که بدیهی است نسبت به حلقه‌ی for، کندتر اجرا خواهد شد:
      List<int>.Enumerator enumerator = this.ItemsList.GetEnumerator();
      try
      {
        while (enumerator.MoveNext())
        {
          int current = enumerator.Current;
        }
      }
      finally
      {
        enumerator.Dispose();
      }
اگر این خروجی را برای دات نت 6 و دات نت 7 تهیه کنیم، به یک جواب خواهیم رسید. یعنی از دیدگاه #Low-level C، تفاوتی بین IL دات نت 6 و 7 از این لحاظ وجود ندارد. تفاوتی اصلی در بهبودهای JIT دات نت 7 است که سبب شده، خروجی نهایی حلقه‌‌های foreach با for یکی باشد.
مطالب
تعیین اعتبار کردن یک عبارت SQL - قسمت دوم

مطلبی را روز قبل نوشتم در مورد تعیین اعتبار یک کوئری. این مورد از آنجایی حائز اهمیت می‌شود که برای مثال تغییری در ساختار یکی از جداول حاصل شود. اکنون می‌خواهیم بررسی کنیم آیا سیستم از کار افتاده یا نه!؟
شما می‌توانید نام یک فیلد را تغییر دهید (حتی اگر این فیلد در یک رویه ذخیره شده استفاده شده باشد) و هیچ خطایی هم نخواهید گرفت و این منشاء دردسرهای زیادی خواهد بود.
در حالت استفاده از SET NOEXEC ON ، کوئری مورد نظر فقط کامپایل می‌شود و همچنین از لحاظ نحوی بررسی خواهد شد، اما این کافی نیست.
مثال زیر را در نظر بگیرید:

Create PROCEDURE Test1
AS
SELECT * FROM tblPIDs1
جدول tblPIDs1 در دیتابیس مورد نظر وجود ندارد.
این کوئری قابل اجرا است. دکمه‌ی F5 را فشار دهید، بلافاصله رویه ذخیره شده‌ی Test1 برای شما ایجاد خواهد شد.
سپس کوئری زیر را اجرا کنید:

USE testdb
SET NOEXEC ON;
exec test1 ;
SET NOEXEC OFF;
بدون مشکل و بروز خطایی، پیغام زیر را نشان می‌دهد:
Command(s) completed successfully

ایرادی هم وارد نیست چون فقط عملیات parsing و compile صورت گرفته و نه اجرای واقعی رویه ذخیره شده. اینجا از لحاظ دستوری مشکلی وجود ندارد.

در این نوع موارد می‌توان از SET FMTONLY ON استفاده کرد. این مورد اجرای غیر واقعی یک کوئری را سبب می‌شود (تاثیری روی دیتابیس موجود نخواهد داشت، برای مثال اگر در رویه ذخیره شما عبارت insert وجود داشت، دیتایی insert نخواهد شد) و تنها متادیتای حاصل را بازگشت می‌دهد. مثلا نام ستون‌های یک کوئری را و همچنین در این حین اگر خطایی رخ داده باشد، آن‌را نیز ارائه خواهد داد.

USE testdb
SET FMTONLY ON;
exec test1 ;
SET FMTONLY OFF;
با اجرای کوئری فوق خطای زیر ظاهر می‌شود:
Msg 208, Level 16, State 1, Procedure test1, Line 3
Invalid object name 'tblPIDs1'.
برای اتوماسیون این توانایی می‌توان از کوئری زیر استفاده کرد:

USE testdb;

SET NOCOUNT ON;

DECLARE @name NVARCHAR(MAX),
@sql NVARCHAR(MAX),
@type CHAR(2), -- object type
@type_desc NVARCHAR(60), -- object type description
@params NVARCHAR(MAX) -- parameters

DECLARE @tblInvalid TABLE (
-- invalid objects
[type_desc] NVARCHAR(60),
[name] NVARCHAR(MAX),
[error_number] INT,
[error_message] NVARCHAR(MAX),
[type] CHAR(2)
);

DECLARE testSPs CURSOR FAST_FORWARD
FOR
SELECT [name] = OBJECT_NAME(SM.[object_id]),
[type] = SO.[type],
SO.[type_desc],
[params] = (
SELECT (
SELECT CONVERT(
XML,
(
SELECT STUFF(
(
SELECT ', ' + [name] +
'=NULL' AS
[text()]
FROM sys.parameters
WHERE [object_id] = SM.[object_id]
FOR XML PATH('')
),
1,
1,
''
)
)
)
FOR XML RAW,
TYPE
).value('/row[1]', 'varchar(max)')
)
FROM sys.sql_modules SM
JOIN sys.objects SO
ON SO.[object_id] = SM.[object_id]
WHERE SO.[is_ms_shipped] = 0
AND SO.[type] = 'P'


OPEN testSPs
FETCH NEXT FROM testSPs INTO @name, @type, @type_desc, @params

WHILE (@@FETCH_STATUS = 0)
BEGIN
BEGIN TRY
SET @sql = 'SET FMTONLY ON; exec ' + @name + ' ' + @params +
'; SET FMTONLY OFF;'
--PRINT @sql;
EXEC (@sql) ;
END TRY
BEGIN CATCH
PRINT @type_desc + ', ' + @name + ', Error: ' + CAST(ERROR_NUMBER() AS VARCHAR)
+ ', ' + ERROR_MESSAGE();
INSERT INTO @tblInvalid
SELECT @type_desc,
@name,
ERROR_NUMBER(),
ERROR_MESSAGE(),
@type

;
END CATCH


FETCH NEXT FROM testSPs INTO @name, @type, @type_desc, @params
END
CLOSE testSPs
DEALLOCATE testSPs


SELECT [type_desc],
[name],
[error_number],
[error_message]
FROM @tblInvalid
ORDER BY
CHARINDEX([type], ' U V PK UQ F TR FN TF P SQ '),
[name];

توضیحات:
این کوئری، در دیتابیس جاری که در قسمت use dbname مشخص می‌شود، تمامی رویه‌های ذخیره شده را به صورت خودکار پیدا می‌کند. سپس لیست آرگومان‌های آن‌ها را نیز یافته و عبارت exec مربوطه را تشکیل می‌دهد. سپس با استفاده از SET FMTONLY ON سعی در شبیه سازی اجرای تک تک رویه‌های ذخیره شده می‌کند. اگر خطایی در این بین رخ داد، آن‌ها را در یک جدول موقتی ذخیره کرده و در آخر نتیجه را نمایش می‌دهد.

ارزش این کوئری زمانی مشخص می‌شود که تعداد زیادی رویه ذخیره شده داشته باشید اما نمی‌دانید کدامیک از آن‌ها بر اساس آخرین تغییرات صورت گرفته، هنوز معتبر هستند یا نه. آیا به قول معروف، سیستم اومد پایین یا خیر!؟

نکته:
قسمتی که از XML استفاده شده جهت concatenating نتیجه حاصل از کوئری، مورد استفاده قرار گرفته و این روزها بحث رایجی است که در بسیاری از سایت‌ها در مورد آن می‌توان مطالب مفیدی را یافت. راه دیگر انجام آن استفاده از COALESCE می‌باشد.


مآخذ:
Check Validity of SQL Server Stored Procedures
Which of your Stored Procedures are no longer Valid
SET FMTONLY ON

اشتراک‌ها
دوره 3 ساعته PostgreSQL

PostgreSQL Tutorial Full Course 2022

I provide here in this PostgreSQL tutorial a full course you can use to master PostgreSQL. Postgres is an object relational database that is just as fast as MySQL that adheres more closely to SQL standards and excels at concurrency. Postgres is also superior at avoiding data corruption.

TABLE OF CONTENTS
00:00 Intro
00:30 Why Use Postgres?
01:13 What is a Database
03:12 Change Database Theme
03:53 Create a Database
04:46 Design a Database
05:50 Turn Invoice into a Database
07:04 Make a Table
12:13 Data Types
16:36 Adding Data to Table
18:15 To See Data
18:25 SELECT
19:19 Create Custom Type
20:48 Change Column Data Type
22:58 Thinking About Tables
25:37 Breaking Up Tables
27:03  Primary & Foreign Keys
32:40 Foreign & Primary Keys
33:28 Altering Tables Many Examples
53:00 Getting Data from One Table
53:40 Where
54:30 Conditional Operators
55:48 Logical Operators
58:12 Order By
59:32 Limit
1:01:45 GROUP BY
1:03:11 Distinct
1:05:00 Getting Data from Multiple Tables
1:05:21 Inner Join
1:08:50 Join 3 Tables
1:13:15  Arithmetic Operators
1:13:45 Join with Where
1:14:55 Outer Joins
1:17:03 Cross Joins
1:18:16 Unions
1:19:27 Extract
1:21:05 IS NULL
1:22:03 SIMILAR LIKE & ~
1:29:25 GROUP BY
1:31:14  HAVING
1:32:18  AGGREGATE FUNCTIONS
1:34:22 WORKING WITH VIEWS
1:45:01 SQL Functions
1:49:00 Dollar Quotes
1:50:06 Functions that Return Void
1:52:38 Get Maximum Product Price
1:53:39 Get Total Value of Inventory
1:54:26 Get Number of Customers
1:56:15 Named Parameters
2:01:30 Return a Row / Composite
2:03:38 Get Multiple Rows
2:07:08 PL/pgSQL
2:11:35 Variables in Functions
2:15:55 Store Rows in Variables
2:19:17 IN INOUT and OUT
2:21:01 Using Multiple Outs
2:25:56 Return Query Results
2:33:42 IF ELSEIF and ELSE
2:38:48  CASE Statement
2:42:01 Loop Statement
2:45:20 FOR LOOP
2:48:34 Result Sets, Blocks & Raise Notice
2:51:11 For Each and Arrays
2:53:20 While Loop
2:54:54 Continue
3:01:34 Stored Procedures
3:09:35 Triggers
3:29:25 Cursors
3:39:45 Installation 

دوره 3 ساعته PostgreSQL
مطالب
مجموعه آموزشی رایگان workflow foundation از مایکروسافت
Intro to Windows Workflow Foundation (Part 1 of 7): Workflow in Windows Applications (Level 100)
This webcast is a code-focused introduction to developing workflow-enabled Microsoft Windows platform applications. We cover the basics of developing, designing, and debugging workflow solutions. Gain the knowledge and insight you need to be confident choosing workflow for everyday applications.


Intro to Windows Workflow Foundation (Part 2 of 7): Simple Human Workflow Using E-mail (Level 200)
Have you thought about how you might apply the workflow concept to e-mail? In this webcast New Zealand based regional director, Chris Auld, leads attendees through a simple worked example of the use of SMTP e-mail as part of a workflow solution. Chris demonstrates how to create custom activities to query Active Directory to retrieve user data, send e-mail, and wait for e-mail responses to continue the workflow process. This code-intensive session gives users taking their first steps with workflow a good grounding in some of the key extensibility concepts.


Intro to Windows Workflow Foundation (Part 3 of 7): Hosting and Communications Options in Workflow Scenarios (Level 300)
The session looks at options for hosting workflow applications. We cover managing events, instance tracking, and persistence, and provide a close look at the simple communications mechanisms that are available for you to use in your workflow applications.


Intro to Windows Workflow Foundation (Part 4 of 7): Workflow, Messaging, and Services: Developing Distributed Applications with Workflows (Level 300)
Web service technologies have typically taken a "do-it-yourself" approach to maintaining the interoperation state of services. Using workflow, developers now have tools that allow them to describe the long-running state of their services and delegate much of the state management to the underlying platform. Managing this state correctly becomes even more challenging in applications that coordinate work across multiple services either within an organization or at an Internet scale. This session looks at how developers who use either Microsoft ASMX or Microsoft's framework for building service-oriented applications, code-named "Indigo", can create workflow-oriented applications that are both faster to write and more manageable and flexible once deployed.


Intro to Windows Workflow Foundation (Part 5 of 7): Developing Event Driven State Machine Workflows (Level 300)
State machines used to be something that you had to first draw on paper and then implement in code. This session shows how to use technologies to create event-driven workflows and how to apply this to a typical programming problem. We introduce the concept of a flexible process and show how this can help with modeling real-world processes using state and sequential workflow. Plenty of coding is included to illustrate how you can seamlessly merge state machine design and your code.


Intro to Windows Workflow Foundation (Part 6 of 7): Extending Workflow Capabilities with Custom Activities (Level 300)
It is helpful to think of activities as controls within a workflow, similar to controls used with Microsoft ASP.NET Pages or Microsoft Windows Forms. You can use activities to encapsulate execution logic, communicate with the host and decompose a workflow into reusable components. This session examines the simple process of creating custom activities. If you want to expose activities to other developers designing workflows, you are likely to find this session valuable.


Intro to Windows Workflow Foundation (Part 7 of 7): Developing Rules Driven Workflows (Level 300)
Rules can be a powerful business tool when combined with workflow. In this session, learn how to develop more advanced activities that support the modeling of rich business behavior such as human workflow. Understand when to use rules for business logic, and see how rule policies allow for the description of sophisticated behavior in an integrated and flexible way. This session gives you an interesting insight into the power of using workflow at the core of a line of business application.