مطالب
پیاده سازی پروژه‌های React با TypeScript - قسمت ششم - تعیین نوع هوک useEffect و هوک‌های سفارشی
در این قسمت قصد داریم یک هوک سفارشی را ایجاد کرده و نوع‌های آن‌را توسط TypeScript مشخص کنیم. همچنین در این بین از هوک useEffect هم استفاده خواهیم کرد؛ هرچند این هوک، نکات تایپ‌اسکریپتی خاصی را به همراه ندارد.


ایجاد هوک سفارشی useClickOutside

برای این منظور فایل جدید src\components\useClickOutside.tsx را ایجاد کرده و به صورت زیر تکمیل می‌کنیم:
import { useEffect } from "react";

const useClickOutside = (ref, handler) => {
  useEffect(() => {
    const listener = (event) => {
      if (!ref.current || ref.current.contains(event.target)) {
        return;
      }
      handler(event);
    };
    document.addEventListener("mousedown", listener);
    document.addEventListener("touchstart", listener);

    return () => {
      document.removeEventListener("mousedown", listener);
      document.removeEventListener("touchstart", listener);
    };
  }, [handler, ref]);
};

export { useClickOutside };
توضیحات:

- متد هوک سفارشی ما، دو پارامتر ref و handler را دریافت می‌کند. ref به DOM Element جاری اشاره می‌کند و handler تابعی است که هنگام کلیک در خارج از ناحیه‌ی یک DOM Element خاص، اجرا می‌شود.
- سپس یک listener را تعریف کرده‌ایم که این تابع handler را اجرا می‌کند؛ البته به شرطی‌که DOM Element ارسالی وجود داشته باشد و خود target هم نباشد.
- در ادامه این listener را به رخ‌دادهای mousedown و touchstart متصل کرده و پاکسازی آن‌ها را هم در قسمت return متد useEffect انجام داده‌ایم.
- همچنین چون می‌خواهیم تنها در صورت تغییر پارامترهای ارسالی به هوک سفارشی جاری، این useEffect به روز رسانی شود، این پارامترها را در قسمت Dependency List مربوط به متد useEffect نیز ذکر کرده‌ایم.

تا اینجا اگر کدهای فوق را دنبال کنید، چون پسوند این فایل tsx است، خطاهای تایپ‌اسکریپتی زیر را مشاهده خواهید کرد که به دلیل انتساب ضمنی نوع any، به این پارامترهای بدون نوع است:



استفاده از هوک سفارشی useClickOutside

بنابراین قدم بعدی کار، تکمیل نوع‌های مرتبط با این پارامترها است. برای این منظور، ابتدا سعی می‌کنیم تا این هوک را در کامپوننت src\components\ReducerButtons.tsx قسمت قبلی استفاده کنیم تا نسبت به نوع پارامترهای ارسالی به این هوک، درک بهتری را پیدا کنیم:
import { useClickOutside } from "./useClickOutside";

// ...

export const ReducerButtons = () => {
  const [state, dispatch] = useReducer(reducer, initialState);

  const ref = useRef<HTMLDivElement>(null);
  useClickOutside(ref, () => {
    console.log("clicked outside");
  });

  return (
    <div ref={ref}>
      // ...
    </div>
  );
};
برای این منظور، سه تغییر را انجام داده‌ایم:
- ابتدا import‌های لازم را به ابتدای ماژول افزوده‌ایم.
- سپس با استفاده از هوک useRef که در قسمت چهارم آن‌را بررسی کردیم، ارجاعی را به المان div رندر شده، بدست آورده‌ایم.
- در آخر هوک سفارشی جدید useClickOutside را فراخوانی کرده‌ایم که آرگومان اول آن به DOM Element مربوط به div اشاره می‌کند و پارامتر دوم آن، تابعی است که پس از کلیک در خارج از ناحیه‌ی آن، اجرا خواهد شد.


تعیین نوع‌های پارامترهای هوک سفارشی

تا اینجا متوجه شدیم که handler، چیزی بجز یک تابع که void را بازگشت می‌دهد (void <= ())، نیست. همچنین نوع شیء ref را هم می‌توان با نزدیک کردن اشاره‌گر ماوس، به متغیر ref در کامپوننت ReducerButtons، مشاهده کرد:


بر این اساس، تعاریف نوع‌های پارامترهای هوک سفارشی useClickOutside به صورت زیر مشخص می‌شوند:
const useClickOutside = (
  ref: React.RefObject<HTMLDivElement>,
  handler: () => void
) => {
همچنین بر اساس نکات قسمت سوم، نوع event را نیز به React.MouseEvent تنظیم می‌کنیم:
const listener = (event: React.MouseEvent<HTMLElement>) => {
پس از آن، اولین خطایی که ظاهر می‌شود به صورت زیر است:


عنوان می‌کند که نوع event.target، از نوع Node، که مورد نظر متد contains است، نیست. برای رفع آن فقط کافی است تبدیل نوع زیر را انجام داد:
ref.current.contains(event.target as Node)
مشکل بعدی، بدون پارامتر تعریف کردن نوع تابع handler است:


برای رفع این خطا، نوع پارامتر تابع handler را نیز بر اساس رویداد ارسالی به آن، مشخص می‌کنیم:
const useClickOutside = (
  ref: React.RefObject<HTMLDivElement>,
  handler: (event: React.MouseEvent<HTMLElement>) => void
) => {
مرحله‌ی آخر، عدم تطابق React.MouseEvent تعریف شده، با پارامترهای متد addEventListener است:


برای درک بهتر این خطا، اشاره‌گر ماوس را به محل تعریف این متد نزدیک می‌کنیم، تا بتوان امضای آن‌را مشاهده کرد. در حالت mousedown، پارامتر دوم این متد، از نوع MouseEvent است:


(method) Document.addEventListener<"mousedown">(type: "mousedown", listener: (this: Document, ev: MouseEvent) 
=> any, options?: boolean | AddEventListenerOptions | undefined): void (+1 overload)
و در حالت touchstart، پارامتر دوم آن به TouchEvent تغییر کرده‌است:
(method) Document.addEventListener<"touchstart">(type: "touchstart", listener: (this: Document, ev: TouchEvent)
 => any, options?: boolean | AddEventListenerOptions | undefined): void (+1 overload)
به همین جهت است که نوع <React.MouseEvent<HTMLElement تعریف شده، با این دو سازگار نیست و خطا رخ‌داده‌است. برای رفع این خطا، با استفاده از union types، هر دو رخ‌داد MouseEvent و TouchEvent را باید به عنوان نوع پارامترهای ورودی تعریف کرد:
const useClickOutside = (
  ref: React.RefObject<HTMLDivElement>,
  handler: (event: MouseEvent | TouchEvent) => void
) => {
  useEffect(() => {
    const listener = (event: MouseEvent | TouchEvent) => {
بنابراین با کمی دقت به تعاریف استانداردی که به همراه متدهای مورد استفاده هستند، می‌توان نوع‌های مرتبط را تشخیص داد و از آن‌ها استفاده کرد.


یک نکته‌ی تکمیلی: در اینجا با تعریف <ref: React.RefObject<HTMLDivElement، دیگر ref ارسالی، هیچ المان دیگری را بجز div نمی‌تواند بپذیرد. برای عمومی‌تر کردن آن، می‌توان بر روی آن کلیک راست کرد و گزینه‌ی Go to definition را انتخاب نمود:


بنابراین حالت عمومی‌تر آن، استفاده از HTMLElement ای است که HTMLDivElement از آن ارث بری کرده‌است:
const useClickOutside = (
  ref: React.RefObject<HTMLElement>,
  handler: (event: MouseEvent | TouchEvent) => void
) => {

با این تغییرات، کدهای نهایی این قسمت، به صورت زیر در خواهند آمد:
import { useEffect } from "react";

const useClickOutside = (
  ref: React.RefObject<HTMLElement>,
  handler: (event: MouseEvent | TouchEvent) => void
) => {
  useEffect(() => {
    const listener = (event: MouseEvent | TouchEvent) => {
      if (!ref.current || ref.current.contains(event.target as Node)) {
        return;
      }
      handler(event);
    };
    document.addEventListener("mousedown", listener);
    document.addEventListener("touchstart", listener);

    return () => {
      document.removeEventListener("mousedown", listener);
      document.removeEventListener("touchstart", listener);
    };
  }, [handler, ref]);
};

export { useClickOutside };
اشتراک‌ها
معرفی Extension ویژوال استدیو 2015 برای تمپلیت های Asp.Net

In Visual Studio 2013, there were a handful of templates that supported developing ASP.NET projects with various frameworks and data structures.  Some of those project templates from the Visual Studio 2012 era have been removed from the Visual Studio 2015 install and added to the Visual Studio Extension gallery as the ASP.NET Project Templates extension for Visual Studio 2015. 

معرفی Extension ویژوال استدیو 2015 برای تمپلیت های Asp.Net
اشتراک‌ها
Metadata Function ها در SQL Server

To be able to make full use of the system catalog to find out more about a database, you need to be familiar with the metadata functions. They save a great deal of time and typing when querying the metadata. Once you get the hang of these functions, the system catalog suddenly seems simple to use, as Robert Sheldon demonstrates in this article. 

Metadata Function ها در SQL Server
مطالب دوره‌ها
ایجاد کاتالوگ‌های Full text search و ایندکس‌های آن
جستجو بر روی خواص و متادیتای اسناد آفیس

همانطور که در قسمت قبل نیز عنوان شد، فیلترهای FTS آفیس، علاوه بر اینکه امکان جستجوی پیشرفته FTS را بر روی کلیه فایل‌های مجموعه آفیس میسر می‌کنند، امکان جستجوی FTS را بر روی خواص ویژه اضافی آن‌ها، مانند نام نویسنده، واژه‌های کلیدی، تاریخ ایجاد و امثال آن نیز به همراه دارند.
اینکه چه خاصیتی را بتوان جستجو کرد نیز بستگی به نوع فیلتر نصب شده دارد. برای تعریف خواص قابل جستجوی یک سند، باید یک SEARCH PROPERTY LIST را ایجاد کرد:
CREATE SEARCH PROPERTY LIST WordSearchPropertyList;
GO

ALTER SEARCH PROPERTY LIST WordSearchPropertyList
   ADD 'Authors'
   WITH (PROPERTY_SET_GUID = 'F29F85E0-4FF9-1068-AB91-08002B27B3D9',
             PROPERTY_INT_ID = 4,
             PROPERTY_DESCRIPTION = 'System.Authors - authors of a given item.');
GO
در این تعریف، PROPERTY_INT_ID و PROPERTY_SET_GUIDها استاندارد بوده و لیست آن‌ها را در آدرس ذیل می‌توانید مشاهده نمائید:


بهبود کیفیت جستجو توسط Stop lists و Stop words

به یک سری از کلمات و حروف، اصطلاحا noise words گفته می‌شود. برای مثال در زبان انگلیسی حروف و کلماتی مانند a، is، the و and به صورت خودکار از FTS حذف می‌شوند؛ چون جستجوی آن‌ها بی‌حاصل است. به این‌ها stop words نیز می‌گویند.
با استفاده از کوئری ذیل می‌توان لیست stop words تعریف شده در بانک اطلاعاتی جاری را مشاهده کرد:
 -- Check the Stopwords list
SELECT w.stoplist_id,
   l.name,
   w.stopword,
   w.language
FROM sys.fulltext_stopwords AS w
   INNER JOIN sys.fulltext_stoplists AS l
     ON w.stoplist_id = l.stoplist_id;
و برای تعریف stop words از دستورات ذیل کمک گرفته می‌شود:
 -- Stopwords list
CREATE FULLTEXT STOPLIST SQLStopList;
GO

-- Add a stopword
ALTER FULLTEXT STOPLIST SQLStopList
 ADD 'SQL' LANGUAGE 'English';
GO


کاتالوگ‌های Full Text Search

ایندکس‌های ویژه‌ی FTS، در مکان‌هایی به نام Full Text Catalogs ذخیره می‌شوند. این کاتالوگ‌ها صرفا یک شیء مجازی بوده و تنها برای تعریف ظرفی دربرگیرنده‌ی ایندکس‌های FTS تعریف می‌شوند. در نگارش‌های پیش از 2012 اس کیوال سرور، این کاتالوگ‌ها اشیایی فیزیکی بودند؛ اما اکنون تبدیل به اشیایی مجازی شده‌اند.
حالت کلی تعریف یک fulltext catalog به نحو ذیل است:
 create fulltext catalog catalog_name
on filegroup filegroup_name
in path  'rootpath'
with some_options
as default
authoriztion owner_name
accent_sensivity = {on|off}
اما اکثر گزینه‌های آن مانند on filegroup و in path صرفا برای حفظ سازگاری با نگارش‌های قبلی حضور دارند و دیگر نیازی به ذکر آن‌ها نیست؛ چون تعریف کننده‌ی ماهیت فیزیکی این کاتالوگ‌ها می‌باشند.
به صورت پیش فرض حساسیت به لهجه یا accent_sensivity خاموش است. اگر روشن شود، باید کل ایندکس مجددا بازسازی شود.


ایجاد ایندکس‌های Full Text

پس از ایجاد یک fulltext catalog، اکنون نوبت به تعریف ایندکس‌هایی فیزیکی هستند که داخل این کاتالوگ‌ها ذخیره خواهند شد:
 -- Full-text catalog
CREATE FULLTEXT CATALOG DocumentsFtCatalog;
GO

-- Full-text index
CREATE FULLTEXT INDEX ON dbo.Documents
(
  docexcerpt Language 1033,
  doccontent TYPE COLUMN doctype
  Language 1033
  STATISTICAL_SEMANTICS
)
KEY INDEX PK_Documents
ON DocumentsFtCatalog
WITH STOPLIST = SQLStopList,
  SEARCH PROPERTY LIST = WordSearchPropertyList,
  CHANGE_TRACKING AUTO;
GO
در اینجا توسط KEY INDEX نام منحصربفرد ایندکس مشخص می‌شود.
CHANGE_TRACKING AUTO به این معنا است که SQL Server به صورت خودکار کار به روز رسانی این ایندکس را با تغییرات رکوردها انجام خواهد داد.
ذکر STATISTICAL_SEMANTICS، منحصر به SQL Server 2012 بوده و کار آن تشخیص واژه‌های کلیدی و ایجاد ایندکس‌های یافتن اسناد مشابه است. برای استفاده از آن حتما نیاز است مطابق توضیحات قسمت قبل، Semantic Language Database پیشتر نصب شده باشد.
توسط STOPLIST، لیست واژه‌هایی که قرار نیست ایندکس شوند را معرفی خواهیم کرد. SQLStopList را در ابتدای بحث ایجاد کردیم.
Language 1033 به معنای استفاده از زبان US English است.
نحوه‌ی استفاده از SEARCH PROPERTY LIST ایی که پیشتر تعریف کردیم را نیز در اینجا ملاحظه می‌کنید.


مثالی برای ایجاد ایندکس‌های FTS

برای اینکه ربط منطقی نکات عنوان شده را بهتر بتوانید بررسی و آزمایش کنید، مثال ذیل را درنظر بگیرید.
ابتدا جدول Documents را برای ذخیره سازی تعدادی سند، ایجاد می‌کنیم:
 CREATE TABLE dbo.Documents
(
  id INT IDENTITY(1,1) NOT NULL,
  title NVARCHAR(100) NOT NULL,
  doctype NCHAR(4) NOT NULL,
  docexcerpt NVARCHAR(1000) NOT NULL,
  doccontent VARBINARY(MAX) NOT NULL,
  CONSTRAINT PK_Documents
PRIMARY KEY CLUSTERED(id)
);
اگر به این جدول دقت کنید، هدف از آن ذخیره‌ی اسناد آفیس است که فیلترهای FTS آن‌را در قسمت قبل نصب کردیم. ستون doctype، معرف نوع سند و doccontent ذخیره کننده‌ی محتوای کامل سند خواهند بود.

سپس اطلاعاتی را در این جدول ثبت می‌کنیم:
-- Insert data
-- First row
INSERT INTO dbo.Documents
(title, doctype, docexcerpt, doccontent)
SELECT N'Columnstore Indices and Batch Processing', 
 N'docx',
 N'You should use a columnstore index on your fact tables,
   putting all columns of a fact table in a columnstore index. 
   In addition to fact tables, very large dimensions could benefit 
   from columnstore indices as well. 
   Do not use columnstore indices for small dimensions. ',
 bulkcolumn
FROM OPENROWSET
 (BULK 'C:\Users\Vahid\Desktop\Updates\fts_docs\ColumnstoreIndicesAndBatchProcessing.docx', 
  SINGLE_BLOB) AS doc;

-- Second row
INSERT INTO dbo.Documents
(title, doctype, docexcerpt, doccontent)
SELECT N'Introduction to Data Mining', 
 N'docx',
 N'Using Data Mining is becoming more a necessity for every company 
   and not an advantage of some rare companies anymore. ',
 bulkcolumn
FROM OPENROWSET
 (BULK 'C:\Users\Vahid\Desktop\Updates\fts_docs\IntroductionToDataMining.docx', 
  SINGLE_BLOB) AS doc;

-- Third row
INSERT INTO dbo.Documents
(title, doctype, docexcerpt, doccontent)
SELECT N'Why Is Bleeding Edge a Different Conference', 
 N'docx',
 N'During high level presentations attendees encounter many questions. 
   For the third year, we are continuing with the breakfast Q&A session. 
   It is very popular, and for two years now, 
   we could not accommodate enough time for all questions and discussions! ',
 bulkcolumn
FROM OPENROWSET
 (BULK 'C:\Users\Vahid\Desktop\Updates\fts_docs\WhyIsBleedingEdgeADifferentConference.docx', 
  SINGLE_BLOB) AS doc;

-- Fourth row
INSERT INTO dbo.Documents
(title, doctype, docexcerpt, doccontent)
SELECT N'Additivity of Measures', 
 N'docx',
 N'Additivity of measures is not exactly a data warehouse design problem. 
   However, you have to realize which aggregate functions you will use 
   in reports for which measure, and which aggregate functions 
   you will use when aggregating over which dimension.',
 bulkcolumn
FROM OPENROWSET
 (BULK 'C:\Users\Vahid\Desktop\Updates\fts_docs\AdditivityOfMeasures.docx', 
  SINGLE_BLOB) AS doc;
GO
4 ردیف ثبت شده در جدول اسناد، نیاز به 4 فایل docx نیز دارند که آن‌ها را از آدرس ذیل می‌توانید برای تکمیل ساده‌تر آزمایش دریافت کنید:
fts_docs.zip

در ادامه می‌خواهیم قادر باشیم تا بر روی متادیتای نویسنده‌ی این اسناد نیز جستجوی کامل FTS را انجام دهیم. به همین جهت SEARCH PROPERTY LIST آن‌را نیز ایجاد خواهیم کرد:
 -- Search property list
CREATE SEARCH PROPERTY LIST WordSearchPropertyList;
GO
ALTER SEARCH PROPERTY LIST WordSearchPropertyList
 ADD 'Authors'
 WITH (PROPERTY_SET_GUID = 'F29F85E0-4FF9-1068-AB91-08002B27B3D9',
PROPERTY_INT_ID = 4,
PROPERTY_DESCRIPTION = 'System.Authors - authors of a given item.');
GO
همچنین می‌خواهیم از واژه‌ی SQL در این اسناد، در حین ساخت ایندکس‌های FTS صرفنظر شود. برای این منظور یک FULLTEXT STOPLIST را به نام SQLStopList ایجاد کرده و سپس واژه‌ی مدنظر را به آن اضافه می‌کنیم:
 -- Stopwords list
CREATE FULLTEXT STOPLIST SQLStopList;
GO
-- Add a stopword
ALTER FULLTEXT STOPLIST SQLStopList
 ADD 'SQL' LANGUAGE 'English';
GO
صحت عملیات آن‌را توسط کوئری «Check the Stopwords list» ذکر شده در ابتدای بحث می‌توانید بررسی کنید.

اکنون زمان ایجاد یک کاتالوگ FTS است:
 -- Full-text catalog
CREATE FULLTEXT CATALOG DocumentsFtCatalog;
GO
با توجه به اینکه در نگارش‌های جدید SQL Server این کاتالوگ صرفا ماهیتی مجازی دارد، ساده‌ترین Syntax آن برای کار ما کفایت می‌کند.
و در آخر ایندکس FTS ایی را که پیشتر در مورد آن بحث کردیم، ایجاد خواهیم کرد:
 -- Full-text index
CREATE FULLTEXT INDEX ON dbo.Documents
(
  docexcerpt Language 1033,
  doccontent TYPE COLUMN doctype
  Language 1033
  STATISTICAL_SEMANTICS
)
KEY INDEX PK_Documents
ON DocumentsFtCatalog
WITH STOPLIST = SQLStopList,
  SEARCH PROPERTY LIST = WordSearchPropertyList,
  CHANGE_TRACKING AUTO;
GO



در این تصویر محل یافتن اجزای مختلف Full text search را در management studio مشاهده می‌کنید.


یک نکته‌ی تکمیلی
برای زبان فارسی نیز یک سری stop words وجود دارند. لیست آن‌ها را از اینجا می‌توانید دریافت کنید:
stopwords.sql
متاسفانه زبان فارسی جزو زبان‌های پشتیبانی شده توسط FTS در SQL Server نیست (نه به این معنا که نمی‌توان با آن کار کرد؛ به این معنا که برای مثال دستورات صرفی زبان را ندارد) و به همین جهت از زبان انگلیسی در اینجا استفاده شده‌است.
مطالب
فراخوانی GraphQL API در یک کلاینت ASP.NET Core
در قسمت قبل، ایجاد کردن Mutation‌ها را در GraphQL تمام کردیم. در این قسمت تصمیم بر این است که از GraphQL API در یک برنامه ASP.NET Core استفاده کنیم. برای فراخوانی GraphQL API در یک برنامه ASP.NET Core، از یک کتابخانه ثالث که به ما در این فرآیند کمک می‌کند استفاده خواهیم کرد.

Preparing the ASP.NET Core Client Project 

کار را با ایجاد کردن یک پروژه ASP.NET Core Web API شروع می‌کنیم :
dotnet new api -n DotNetGraphQLClient
 
به محض اینکه پروژه را ایجاد کردیم فایل launchsettings.json را باز می‌کنیم و مقدار خصوصیت launchBrowser را به false  و خصوصیت applicationUrl  را به
https://localhost:5003;http://localhost:5004
تغییر می‌دهیم. در ادامه فایل appsettings.json را مطابق زیر ویرایش می‌کنیم (اضافه کردن کلید GraphQLURI ):
{
  "Logging": {
    "LogLevel": {
      "Default": "Warning"
    }
  },
  "GraphQLURI": "https://localhost:5001/graphql",
  "AllowedHosts": "*"
}

اکنون می‌توانیم کتابخانه مورد نیاز را نصب کنیم. در ترمینال مربوط به VS Code دستور زیر را وارد کنید:
dotnet add package GraphQL.Client
بعد از نصب، برای ثبت آن، کلاس Startup را مطابق زیر ویرایش کنید:
public void ConfigureServices(IServiceCollection services)
{
    services.AddScoped(x => new GraphQL.Client.GraphQLClient(Configuration["GraphQLURI"]));
   ...
}

در سازنده کلاس GraphQLClient، آدرس endpoint را معرفی می‌کنیم (که در فایل appsettings.json می باشد) .
قدم بعد، ایجاد کردن یک کلاس به نام OwnerConsumer است که عملیات مربوط به فراخوانی API، در این کلاس می‌باشد. 
public class OwnerConsumer
{
    private readonly GraphQL.Client.GraphQLClient _client;
 
    public OwnerConsumer(GraphQL.Client.GraphQLClient client)
    {
        _client = client;
    }
}
سپس کلاس ایجاد شده را در متد ConfigureServices از کلاس Startup  ثبت می‌کنیم: 
public void ConfigureServices(IServiceCollection services)
{
    services.AddScoped(x => new GraphQL.Client.GraphQLClient(Configuration["GraphQLURI"]));
    services.AddScoped<OwnerConsumer>();
    ...
}


Creating Model Classes 
 
در قسمت اول تعدادی کلاس را در پوشه Models  ایجاد کردیم. همه آن کلاس‌ها را برای این client application نیاز داریم؛ پس آن‌ها را ایجاد می‌کنیم: 
public enum TypeOfAccount
{
    Cash,
    Savings,
    Expense,
    Income
}
public class Account
{
    public Guid Id { get; set; }
    public TypeOfAccount Type { get; set; }
    public string Description { get; set; }
}
public class Owner
{
    public Guid Id { get; set; }
    public string Name { get; set; }
    public string Address { get; set; }
 
    public ICollection<Account> Accounts { get; set; }
}

در قسمت سوم ( GraphQL Mutation ) یک کلاس Input type را برای اکشن‌های Mutation ایجاد کردیم. این کلاس هم مورد نیاز می‌باشد. پس آن را ایجاد می‌کنیم:
public class OwnerInput
{
    public string Name { get; set; }
    public string Address { get; set; }
}
اکنون همه چیز آماده است تا Query و Mutation ‌ها را ایجاد کنیم. 

Creating Queries and Mutations to Consume GraphQL API 

کلاس OwnerConsumer را باز می‌کنیم و متد GetAllOwners  را به آن اضافه می‌کنیم:
public async Task<List<Owner>> GetAllOwners()
{
    var query = new GraphQLRequest
    {
        Query = @"
                query ownersQuery{
                  owners {
                    id
                    name
                    address
                    accounts {
                      id
                      type
                      description
                    }
                  }
                }"
    };
 
    var response = await _client.PostAsync(query);
    return response.GetDataFieldAs<List<Owner>>("owners");
}

در ابتدا یک شیء جدید از نوع GraphQLRequest را ایجاد می‌کنیم که شامل خصوصیت Query که برای ارسال queryی که می‌خواهیم به GraphQL API ارسال کنیم، می‌باشد. این query مثل همانی که در ابزار UI.Playground اجرا کردیم، می‌باشد. جهت اجرای این query، متد PostAsync را فراخوانی می‌کنیم که یک پاسخ را برگشت می‌دهد. در نهایت می‌توان نتیجه را تبدیل به نوع مورد نیاز، با استفاده از متد GetDataFieldsAs کرد. 
توجه کنید که آرگومان متد GetDataFieldAs باید با نام query مطابقت داشته باشد .


Implementing a Controller 
یک کنترلر جدید را به نام OwnerController ایجاد می‌کنیم و آن را مطابق زیر ویرایش می‌کنیم: 
[Route("api/owners")]
public class OwnerController: Controller
{
    private readonly OwnerConsumer _consumer;
 
    public OwnerController(OwnerConsumer consumer)
    {
        _consumer = consumer;
    }
 
    [HttpGet]
    public async Task<IActionResult> Get()
    {
        var owners = await _consumer.GetAllOwners();
        return Ok(owners);
    }
}

مثال ضمیمه شده در پایان قسمت قبل را اجرا کنید که بر روی پورت 5001 می‌باشد و سپس پروژه را اجرا کنید ( بر روی پورت 5003 است ).
اکنون می‌توانیم آن را در Postman تست کنیم.
جهت بازیابی تمامی owner ‌ها، در خواست زیر را در Postman صادر کنید:


جهت ایجاد یک owner جدید، کلاس OwnerConsumer را مطابق زیر ویرایش می‌کنیم ( اضافه کردن متد  CreateOwner ) :
 public async Task<Owner> CreateOwner(OwnerInput ownerToCreate)
 {
     var query = new GraphQLRequest
     {
         Query = @"
         mutation($owner: ownerInput!){
           createOwner(owner: $owner){
             id,
             name,
             address
           }
         }",
         Variables = new { owner = ownerToCreate }
     };

     var response = await _client.PostAsync(query);
     return response.GetDataFieldAs<Owner>("createOwner");
 }
و سپس ، کلاس OwnerController را باز می‌کنیم و متد PostItem را به آن اضافه می‌کنیم :
[HttpPost]
public async Task<IActionResult> PostItem([FromBody]OwnerInput model)
{
    var result = await _consumer.CreateOwner(model);
    return Json(result);
}

 اکنون، در خواست ایجاد یک owner جدید را در Postman به صورت زیر صادر می‌کنیم: 


همانطور که مشخص است، همه چیز به درستی کار می‌کند. شما می‌توانید مابقی Query و Mutation ‌ها را با استفاده از Postman تست کنید.

نتیجه گیری 
در این قسمت یادگرفتیم که چگونه :
  1. یک کلاینت ASP.NET Core را برای فراخوانی GraphQL API  آماده سازی کنیم. 
  2. چه کتابخانه‌هایی مورد نیاز است که می‌توانند به ما در انجام فرآیند فراخوانی GraphQL API کمک کنند.
  3. ایجاد کردن query‌ها و mutation‌ها در یک کلاینت ASP.NET Core. 

کد‌های کامل این قسمت را از اینجا دریافت کنید: DotNetGraphQLClient_4.zip 
کد‌های کامل قسمت قبل را می‌توانید از اینجا دریافت کنید:  ASPCoreGraphQL_3.rar

پیشنهادها
نحوه‌ی صحیح کار کردن با بوت استرپ
استفاده از فریم ورک‌های CSS هرچند امکان استفاده مجدد از کدها را به همراه یک سری از نکات پیشرفته‌ی طراحی، به صورت خودکار فراهم می‌کنند، اما ... در دراز مدت مانند یک ویروس در سراسر کدهای صفحات برنامه پخش می‌شوند. نیاز است این مساله را کنترل کرد.

منابع پیشنهادی
اشتراک‌ها
Bootstrap Icons v1.7.0 منتشر شد

Bootstrap Icons v1.7.0 is here with 120 new and updated icons, taking us over 1,500 total icons for the project! It’s the largest update since the initial release, so keep reading to see what’s new. 

Bootstrap Icons v1.7.0 منتشر شد
اشتراک‌ها
101 مثال از LINQ در اندروید

This project contains the C# 101 LINQ Samples rewritten to Kotlin. Like the existing Java LINQ Examples, Kotlin examples run and have their results displayed inside an Android App courtesy of the rich support for Kotlin in Android Studio. 

101 مثال از LINQ در اندروید