چرا XML و چرا پشتیبانی توکار از آن در SQL Server
فیلدهای XML از سال 2005 به امکانات توکار SQL Server اضافه شدهاند و بسیاری از مزایای دنیای NoSQL را درون SQL Server رابطهای مهیا میسازند. برای مثال با تعریف یک فیلد به صورت XML، میتوان از هر ردیف به ردیفی دیگر، اطلاعات متفاوتی را ذخیره کرد؛ به این ترتیب امکان کار با یک فیلد که میتواند اطلاعات یک شیء را قبول کند و در حقیقت امکان تعریف اسکیمای پویا و متغیر را در کنار امکانات یک بانک اطلاعاتی رابطهای که از اسکیمای ثابت پشتیبانی میکند، میسر میشود.
همچنین SQL Server در این حالت قابلیتی را ارائه میدهد که در بسیاری از بانکهای اطلاعاتی NoSQL میسر نیست. در اینجا در صورت نیاز و لزوم میتوان اسکیمای کاملا مشخصی را به یک فیلد XML نیز انتساب داد؛ هر چند این مورد اختیاری است و میتوان یک un typed XML را نیز بکار برد. به علاوه امکانات کوئری گرفتن توکار از این اطلاعات را به کمک XPath ترکیب شده با T-SQL، نیز فراموش نکنید.
بنابراین اگر یکی از اهداف اصلی گرایش شما به سمت دنیای NoSQL، استفاده از امکان تعریف اطلاعاتی با اسکیمای متغیر و پویا است، فیلدهای نوع XML اس کیوال سرور را مدنظر داشته باشید.
یک مثال عملی: فناوری Azure Dev Fabric's Table Storage (نسخه Developer ویندوز Azure که روی ویندوزهای معمولی اجرا میشود؛ یک شبیه ساز خانگی) به کمک SQL Server و فیلدهای XML آن طراحی شده است.
چرا XML و چرا پشتیبانی توکار از آن در SQL Server
یک سند XML معمولا بیشتر از یک قطعه داده را در خود نگهداری میکند و نوع دادهی پیچیده محسوب میشود؛ برخلاف دادههایی مانند int یا varchar که نوعهایی ساده بوده و تنها یک قطعه از اطلاعات خاصی را در خود نگهداری میکنند. بنابراین شاید این سؤال مطرح شود که چرا از این نوع داده پیچیده در SQL Server پشتیبانی شدهاست؟
- از سالهای نسبتا دور، از XML برای انتقال دادهها بین سیستمها و سکوهای کاری مختلف استفاده شدهاست.
- استفادهی گستردهای در برنامههای تجاری دارد.
- بسیاری از فناوریهای موجود از آن پشتیبانی میکنند.
برای مثال اگر با فناوریهای مایکروسافتی کار کرده باشید، به طور قطع حداقل در یک یا چند قسمت از آنها، مستقیما از XML استفاده شدهاست.
بنابراین با توجه به اهمیت و گستردگی استفاده از آن، بهتر است پشتیبانی توکاری نیز از آن داخل موتور یک بانک اطلاعاتی، پیاده سازی شده باشد. این مساله سهولت تهیه پشتیبانهای خودکار، بازیابی آنها و امنیت یکپارچه با SQL Server را به همراه خواهد داشت؛ به همراه تمام زیرساختهای مهیای در SQL Server.
روشهای مختلف ذخیره سازی XML در بانکهای اطلاعاتی رابطهای
الف) ذخیره سازی متنی
این روش نیاز به نگارش خاصی از SQL Server یا بانک اطلاعاتی الزاما خاصی نداشته و با تمام بانکهای اطلاعاتی رابطهای سازگار است؛ مثلا از فیلدهای varchar برای ذخیره سازی آن استفاده شود. مشکلی که این روش به همراه خواهد داشت، از دست دادن ارزش یک سند XML و برخورد متنی با آن است. زیرا در این حالت برای تعیین اعتبار آن یا کوئری گرفتن از آنها نیاز است اطلاعات را از بانک اطلاعاتی خارج کرده و در لایهای دیگر از برنامه، کار جستجو پردازش آنها را انجام داد.
ب) تجزیه XML به چندین جدول رابطهای
برای مثال یک سند XML را درنظر بگیرید که دارای اطلاعات شخص و خریدهای او است. میتوان این سند را به چندین فیلد در چندین جدول مختلف رابطهای تجزیه کرد و سپس با روشهای متداول کار با بانکهای اطلاعاتی رابطهای از آنها استفاده نمود.
ج) ذخیره سازی آنها توسط فیلدهای خاص XML
در این حالت با استفاده از فیلدهای ویژه XML میتوان از فناوریهای مرتبط با XML تمام و کمال استفاده کرد. برای مثال تهیه کوئریهای پیچیده داخل همان بانک اطلاعاتی بدون نیاز به تجزیه سند به چندین جدول و یا خارج کردن آنها از بانک اطلاعاتی و جستجوی بر روی آنها در لایهای دیگر از برنامه.
موارد کاربرد XML در SQL Server
کاربردهای مناسب
- اطلاعات، سلسله مراتبی و تو در تو هستند. XQuery و XPath در این موارد بسیار خوب عمل میکند.
- ساختار قسمتی از اطلاعات ثابت است و قسمتی از آن خیر. برای نمونه، یک برنامهی فرم ساز را درنظر بگیرید که هر فرم آن هر چند دارای یک سری خواص ثابت مانند نام، گروه و امثال آن است، اما هر کدام دارای فیلدهای تشکیل دهنده متفاوتی نیز میباشد. به این ترتیب با استفاده از یک فیلد XML، دیگری نیازی به نگران بودن در مورد نحوه مدیریت اسکیمای متغیر مورد نیاز، نخواهد بود.
نمونهی دیگر آن ذخیره سازی خواص متغیر اشیاء است. هر شیء دارای یک سری خواص ثابت است اما خواص توصیف کنندهی آنها از هر رکورد به رکوردی دیگر متفاوت است.
کاربردهای نامناسب
- کل اطلاعات را داخل فیلد XML قرار دادن. هدف از فیلدهای XML قرار دادن یک دیتابیس داخل یک سلول نیست.
- ساختار تعریف شده کاملا مشخص بوده و به این زودیها هم قرار نیست تغییر کند. در این حالت استفاده از قابلیتهای رابطهای متداول SQL Server مناسبتر است.
- قرار دادن اطلاعات باینری بسیار حجیم در سلولهای XML ایی.
تاریخچهی پشتیبانی از XML در نگارشهای مختلف SQL Server
الف) SQL Server 2000
در SQL Server 2000 روش (ب) توضیح داده شده در قسمت قبل، پشتیبانی میشود. در آن برای تجزیه یک سند XML به معادل رابطهای آن، از تابعی به نام OpenXML استفاده میشود و برای تبدیل این اطلاعات به XML از روش Select … for XML میتوان کمک گرفت. همچنین تاحدودی مباحث XPath Queries نیز در آن گنجانده شدهاست.
ب) SQL Server 2005
در نگارش 2005 آن، برای اولین بار نوع دادهای ویژه XML معرفی گشت به همراه امکان تعریف اسکیمای XML و اعتبارسنجی آن و پشتیبانی از XQuery برای جستجوی سریع بر روی دادههای XML داخل همان بانک اطلاعاتی، بدون نیاز به استخراج اطلاعات XML و پردازش مجزای آنها در لایهای دیگر از برنامه.
ج) SQL Server 2008 به بعد
در اینجا فاز نگهداری این نوع داده خاص شروع شده و بیشتر شامل یک سری بهبودهای کوچک در کارآیی و نحوهی استفاده از آنها میشود.
استفاده از XML با کمک SQLCLR
از SQL Server 2005 به بعد، امکان استفاده از کلیهی امکانات موجود در فضای نام System.Xml دات نت، در SQL Server نیز به کمک SQL CLR مهیا شدهاست. همچنین از SQL Server 2008 به بعد، امکانات فضای نام System.Xml.Linq و مباحث LINQ to XML نیز توسط SQL CLR پشتیبانی میشوند.
البته این امکانات در SQL Server 2005 نیز قابل استفاده هستند، اما اسمبلی شما unsafe تلقی میشود. پس از آزمایشات و بررسی کافی، فضای نام مرتبط با LINQ to XML و امکانات آن، به عنوان اسمبلیهایی امن و قابل استفاده در SQL Server 2008 به بعد، معرفی شدهاند.
مزایای وجود فیلد ویژه XML در SQL Server
پس از اینکه فیلدهای XML به صورت یک نوع داده بومی بانک اطلاعاتی SQL Server معرفی شدند، مزایای ذیل بلافاصله در اختیار برنامه نویسها قرار گرفت:
- امکان تعریف آنها به صورت یک ستون جدولی خاصی
- استفاده از آنها به عنوان یک پارامتر رویههای ذخیره شده
- امکان تعریف خروجی توابع scalar سفارشی تعریف شده به صورت XML
- امکان تعریف متغیرهای T-SQL از نوع XML
برای مثال در اینجا نحوهی تعریف یک جدول جدید دارای فیلدی از نوع XML را مشاهده میکنید:
CREATE TABLE xml_tab ( id INT, xml_col XML )
- امکان تعریف ایندکسهای XML ایی اضافه شدهاست.
چه نوع XML ایی را میتوان در فیلدهای XML ذخیره کرد؟
فیلدهای XML امکان ذخیره سازی دادههای XML خوش فرم را مطابق استاندارد یک XML، دارند. حداکثر اندازه قابل ذخیره سازی در یک فیلد XML دو گیگابایت است.
البته امکانات مهیای در SQL Server در بسیاری از موارد فراتر از استاندارد یک XML هستند. به این معنا که در فیلدهای XML میتوان Documents و یا Fragments را ذخیره سازی کرد. یک سند XML یا Document حاوی تنها یک ریشه اصلی است؛ اما یک Fragment میتواند بیش از یک ریشه اصلی را در خود ذخیره کند. یک مثال:
DECLARE @xml_tab TABLE (xml_col XML) -- document INSERT @xml_tab VALUES ('<person/>') -- fragment INSERT @xml_tab VALUES ('<person/><person/>') SELECT * FROM @xml_tab
DECLARE @xml_tab TABLE (xml_col XML) -- text only INSERT @xml_tab VALUES ('data data data .....') -- empty string INSERT @xml_tab VALUES ('') -- null value INSERT @xml_tab VALUES (null) SELECT * FROM @xml_tab
به علاوه باید دقت داشت که در SQL Server نوع دادهای XML برای ذخیره سازی دادهها بکار گرفته میشود. به این معنا که در اینجا پیشوندهای فضاهای نام XML بیمعنا هستند.
DECLARE @xml_tab TABLE (xml_col XML) INSERT @xml_tab VALUES ('<doc/>') INSERT @xml_tab VALUES ('<doc xmlns="http://www.doctors.com"/>') -- این سه سطر در عمل یکی هستند INSERT @xml_tab VALUES ('<doc xmlns="http://www.documents.com"/>') INSERT @xml_tab VALUES ('<dd:doc xmlns:dd="http://www.documents.com"/>') INSERT @xml_tab VALUES ('<rr:doc xmlns:rr="http://www.documents.com"/>') SELECT * FROM @xml_tab
Encoding ذخیره سازی دادههای XML
SQL Server امکان ذخیره سازی اطلاعات متنی را به فرمت UFT8، اسکی و غیره، دارد. اما جهت پردازش فیلدهای XML و ذخیره سازی آنها از Collation پیش فرض بانک اطلاعاتی کمک خواهد گرفت. البته ذخیره سازی نهایی آن همیشه با فرمت UCS2 است (یونیکد دو بایتی).
DECLARE @xml_tab TABLE (id INT, xml_col XML) INSERT INTO @xml_tab VALUES ( 5, N'<?xml version="1.0" encoding="utf-8"?> <doc1> <row name="vahid"></row> </doc1> ')
XML parsing: line 1, character 38, unable to switch the encoding
برای حل این مشکل باید N ابتدای رشته را حذف کرد. روش دوم، معرفی و استفاده از utf-16 است بجای utf-8 در ویژگی encoding.
همچنین در این حالت اگر encoding را utf-16 معرفی کنیم و ابتدای رشته در حال ذخیره سازی N قرار نگیرد، باز با خطای unable to switch the encoding مواجه خواهیم شد.
نحوهی ذخیره سازی اطلاعات XML ایی در SQL Server
SQL Server فرمت اطلاعات XML وارد شده را حفظ نمیکند. برای مثال اگر قطعه کد زیر را اجرا کنید
DECLARE @xml_tab TABLE (id INT, xml_col XML) INSERT INTO @xml_tab VALUES ( 5, '<?xml version="1.0" encoding="utf-8"?><doc1><row name="vahid"></row></doc1>' ) SELECT * FROM @xml_tab
<doc1> <row name="vahid" /> </doc1>
ذخیره سازی دادههایی حاوی کاراکترهای غیرمجاز XML
اطلاعات دنیای واقعی همیشه به همراه اطلاعات تک کلمهای ساده نیست. ممکن است نیاز شود انواع و اقسام حروف و تگها نیز در این بین به عنوان داده ذخیره شوند. روش حل استاندارد آن بدون نیاز به دستکاری اطلاعات ورودی، استفاده از CDATA است:
DECLARE @xml_tab TABLE (id INT, xml_col XML) INSERT INTO @xml_tab VALUES ( 5, '<person><![CDATA[ 3 > 2 ]]></person>' ) SELECT * FROM @xml_tab
<person> 3 > 2 </person>
محدودیتهای فیلدهای XML
- امکان مقایسه مستقیم را ندارند؛ بجز مقایسه با نال. البته میتوان XML را تبدیل به مثلا varchar کرد و سپس این داده رشتهای را مقایسه نمود. برای مقایسه با null توابع isnull و coalesce نیز قابل بکارگیری هستند.
- order by و group by بر روی این فیلدها پشتیبانی نمیشود.
- به عنوان ستون کلید قابل تعریف نیست.
- به صورت منحصربفرد و unique نیز قابل علامتگذاری و تعریف نیست.
- فیلدهای XML نمیتوانند دارای collate باشند.
منابع مطالعاتی مرتبط
این موضوع اینقدر مهم است که تابحال چندین کتاب در مورد آن تالیف شده است:
Temporal Data & the Relational Model
Trees and Hierarchies in SQL
Developing Time-Oriented Database Applications in SQL
Temporal Data: Time and Relational Databases
Temporal Database Entries for the Springer Encyclopedia of Database Systems
Temporal Database Management
نکته مهمی که در این مآخذ وجود دارند، واژه کلیدی «Temporal data » است که میتواند در جستجوهای اینترنتی بسیار مفید واقع شود.
بررسی ابعاد زمان
فرض کنید کارمندی را استخدام کردهاید که ساعتی 2000 تومان از ابتدای فروردین ماه حقوق دریافت میکند. حقوق این شخص از ابتدای مهرماه قرار است به ساعتی 2400 تومان افزایش یابد. اگر مامور مالیات در بهمن ماه در مورد حقوق این شخص سؤال پرسید، ما چه پاسخی را باید ارائه دهیم؟ قطعا در بهمن ماه عنوان میکنیم که حقوقش ساعتی 2400 تومان است؛ اما واقعیت این است که این عدد از ابتدای استخدام او ثابت نبوده است و باید تاریخچه تغییرات آن، در نحوه محاسبه مالیات سال جاری لحاظ شود.
بنابراین در مدل سازی این سیستم به دو زمان نیاز داریم:
الف) actual time یا زمان رخ دادن واقعهای. برای مثال حقوق شخصی در تاریخ ابتدای مهر ماه تغییر کرده است. به این تاریخ در منابع مختلف Valid time نیز گفته میشود.
ب) record time یا زمان ثبت یک واقعه؛ مثلا زمان پرداخت حقوق. به آن Transaction time هم گفته شده است.
یک مثال:
record date actual date حقوق دریافتی 1392/01/01 1392/01/01 2000/روز 1392/02/01 1392/01/01 2000/روز ... 1392/07/01 1392/07/01 2400/روز ... 1392/17/01 1392/07/01 2400/روز
به ترکیب Valid Time و Transaction Time، اصطلاحا Bitemporal data میگویند.
مشکلات طراحیهای متداول اطلاعات وابسته به زمان
در طراحیهای متداول، عموما یک جدول کارمندان وجود دارد و یک جدول لیست حقوقهای پرداختی. رکوردهای لیست حقوقهای پرداختی نیز توسط یک کلید خارجی به اطلاعات هر کارمند متصل است؛ از این جهت که نمیخواهیم اطلاعاتی تکراری را در جدول لیست حقوقی ثبت کنیم و طراحی نرمال سازی شدهای مدنظر میباشد.
خوب؛ اول مهرماه حقوق شخصی تغییر کرده است. بنابراین کارمند بخش مالی اطلاعات شخص را به روز میکند. با این کار، کل سابقه حقوقهای پرداختی شخص نیز از بین خواهد رفت. چون وجود این کلید خارجی به معنای استفاده از آخرین اطلاعات به روز شده یک کارمند در جدول لیست حقوقی است. الان اگر از جدول لیست حقوقی گزارش بگیریم، کارمندان همواره از آخرین حقوق به روز شده خودشان استفاده خواهند کرد.
راه حلهای متفاوت مدل سازی اطلاعات وابسته به زمان
برای رفع این مشکل مهم، راه حلهای متفاوتی وجود دارند که در ادامه آنها را بررسی خواهیم کرد.
الف) نگهداری اطلاعات وابسته به زمان در جداول نهایی مرتبط
اگر حقوق پایه شخص در زمانهای مختلف تغییر میکند، بهتر است عدد نهایی این حقوق پرداختی نیز در یک فیلد مشخص، در همان جدول لیست حقوقی ثبت شود. این مورد به معنای داشتن «دادهای تکراری» نیست. از این جهت که دادهای تکراری است که اطلاعات آن در تمام زمانها، دارای یک مقدار و مفهوم باشد و اطلاعات حقوق یک شخص اینچنین نیست.
ب) نگهداری اطلاعات تغییرات حقوقی در یک جداول جداگانه
یک جدول ثانویه حقوق جاری کارمندان مرتبط با جدول اصلی کارمندان باید ایجاد شود. در این جدول هر رکورد آن باید دارای بازه زمانی (valid_start_time و valid_end_time) مشخصی باشد. مثلا از تاریخ X تا تاریخ Y، حقوق کارمند شماره 11 ، 2000 تومان در ساعت بوده است. از تاریخ H تا تاریخ Z اطلاعات دیگری ثبت خواهند شد. به این ترتیب با گزارشگیری از جدول لیست حقوقهای پرداخت شده، سابقه گذشته اشخاص محو نشده و هر رکورد بر اساس قرارگیری در یک بازه زمانی ثبت شده در جدول ثانویه حقوق جاری کارمندان تفسیر میشود.
در این حالت باید دقت داشت که بازههای زمانی تعریف شده، با هم تداخل نکنند و برنامه ثبت کننده اطلاعات باید این مساله را به ازای هر کارمند کنترل کند و یا با ثبت record_date، اجازه ثبت بازههای تکراری را نیز بدهد (توضیحات در قسمت بعد).
به این جدول، یک Temporal table نیز گفته میشود. نمونه دیگر آن، نگهداری قیمت یک کالا است از یک تاریخ تا تاریخی مشخص. به این ترتیب میتوان کوئری گرفت که بستنی مگنوم فروخته شده در ماه آبان سال قبل، بر مبنای قیمت آن زمان، دقیقا چقدر فروش کرده است و نه اینکه صرفا بر اساس آخرین قیمت روز این کالا گزارشگیری کنیم که در این حالت اطلاعات نهایی استخراج شده صحیح نیستند.
حال اگر به این طراحی در جدولی دیگر Transaction time یا زمان ثبت یک رکورد یا زمان ثبت یک فروش را هم اضافه کنیم، به جداول حاصل Bitemporal Tables میگویند.
مدیریت به روز رسانیها در جداول Temporal
در جداول Temporal، حذف فیزیکی اطلاعات مطلقا ممنوع است؛ چون سابقه سیستم را تخریب میکند. اگر اطلاعاتی در این جداول دیگر معتبر نیست باید تنها تاریخ پایان دوره آن به روز شوند یا یک رکورد جدید بر اساس بازهای جدید ثبت گردد.
همچنین به روز رسانیها در این جداول نیز معادل هستند با یک Insert جدید به همراه فیلد record_date و نه به روز رسانی واقعی یک رکورد قبلی (شبیه به سیستمهای حسابداری باید عمل کرد).
یک مثال:
فرض کنید حقوق کارمندی که مثال زده شد، در مهرماه به ساعتی 2400 تومان افزایش یافته است و حقوق نهایی نیز پرداخته شده است. بعد از یک ماه مشخص میشود که مدیر عامل سیستم گفته بوده است که ساعتی 2500 تومان و نه ساعتی 2400 تومان! (از این نوع مسایل در دنیای واقعی زیاد رخ میدهند!) خوب؛ اکنون چه باید کرد؟ آیا باید رفت و رکورد ساعتی 2400 تومان را به روز کرد؟ خیر. چون سابقه پرداخت واقعی صورت گرفته را تخریب میکند. به روز رسانی شما ابدا به این معنا نخواهد بود که دریافتی واقعی شخص در آن تاریخ خاص، ساعتی 2500 بوده است.
بنابراین در جداول Temporal، تنها «تغییرات افزودنی» مجاز هستند و این تغییرات همواره به عنوان آخرین رکورد جدول ثبت میشوند. به این ترتیب میتوان اصطلاحا «مابه التفاوت» حقوق پرداخت نشده را به شخص خاصی، محاسبه و پرداخت کرد (میدانیم در یک بازه زمانی خاص به او چقد حقوق دادهایم. همچنین میدانیم که این بازه در یک record_date دیگر لغو و با عددی دیگر، جایگزین شدهاست).
برای مطالعه بیشتر
Bitemporal Database Table Design - The Basics
Temporal Data Techniques in SQL
Database Design: A Point in Time Architecture
Temporal database
Temporal Patterns
راه حلی دیگر؛ استفاده از بانکهای اطلاعاتی NoSQL
بانکهای اطلاعاتی NoSQL برخلاف بانکهای اطلاعاتی رابطهای برای اعمال Read بهینه سازی میشوند و نه برای Write. در چند دهه قبل که بانکهای اطلاعاتی رابطهای پدیدار شدند، یک سخت دیسک 10 مگابایتی حدود 4000 دلار قیمت داشته است. به همین جهت مباحث نرمال سازی اطلاعات و ذخیره نکردن اطلاعات تکراری تا این حد در این نوع بانکهای اطلاعاتی مهم بوده است. اما در بانکهای اطلاعاتی NoSQL امروزی، اگر قرار است فیش حقوقی شخصی ثبت شود، میتوان کل اطلاعات جاری او را یکجا داخل یک سند ثبت کرد (از اطلاعات شخص در آن تاریخ تا اطلاعات تمام اجزای فیش حقوقی در قالب یک شیء تو در توی JSON). به همین جهت بسیار سریع هستند برای اعمال Read و گزارشگیری. همچنین این نوع سیستمها برای نگهداری نگارشهای مختلف یک سند بهینه سازی شدهاند و جزو ساختار توکار آنها است. بنابراین در این نوع سیستمها اگر نیاز است از یک سند خاصی گزارش بگیریم، دقیقا اطلاعات همان تاریخ خاص را دارا است و اگر اطلاعات پایه سیستم را به روز کنیم، از امروز به بعد در سندهای جدید ثبت خواهد شد. این نوع سیستمها رابطهای نیستند و بسیاری از مباحث نرمال سازی اطلاعات در آنها ضرورتی ندارد. قرار است یک فیش حقوقی شخص را نمایش دهیم؟ خوب، چرا تمام اطلاعات مورد نیاز او را در قالب یک شیء JSON تو در توی حاضر و آماده نداشته باشیم؟
سلام
(نوع stub همانند فریم ورک mock میباشد )
تعریفی که از stup تو راهنماش اومده با مطلبی که شما ذکر کردید متفاوته
Martin Fowler’s article Mocks aren’t Stubs compares and contrasts the underlying principles of Stubs and Mocks. As outlined in Martin Fowler’s article, a stub provides static canned state which results in state verification of the system under test, whereas a mock provides a behavior verification of the results for the system under test and their indirect outputs as related to any other component dependencies while under test
به رسم هرساله، سایت معتبر Db-Engines از بین بیش از سیصد بانک اطلاعاتی موجود دنیا، دیتابیسی که در سال گذشته بیشترین محبوبیت را در بین جامعه توسعه دهندگان و مهندسان داده داشته است در ابتدای سال جدید میلادی معرفی کرد.
نگاهی به تغییرات ASP.NET 5 RC 1
از PDF.js برای اینکار استفاده میکنند. همچنین اگر از یک download manager استفاده میکنید، نباید دریافت فایلهای PDF در آن انتخاب شده باشد. برای آزمایش
آموزش برنامه نویسی اندروید
یک راه خوب برای دور زدن تحریم و به روز رسانی مستقیم از طریق SDK Manager