اشتراک‌ها
مقایسه تکنولوژی استفاده شده در شرکت های بزرگ برنامه نویسی دنیا

توسط این سایت می‌توانید بهترین تکنولوژی، ابزار‌ها و فریمورک‌های مورد استفاده در شرکت‌های بزرگ برنامه نویسی دنیا را مشاهده و با یکدیگر مقایسه کنید.

مثلا لینک زیر تکنولوژی‌ها و ابزاری‌های مورد استفاده در شرکت Airbnb را نمایش می‌دهد.

https://stackshare.io/airbnb/airbnb

 

مقایسه تکنولوژی استفاده شده در شرکت های بزرگ برنامه نویسی دنیا
مطالب
نمایش ساختارهای درختی در Blazor
یکی از نکات جالب رندر کامپوننت‌ها در Blazor، امکان فراخوانی بازگشتی آن‌ها است؛ یعنی یک کامپوننت می‌تواند خودش را نیز فراخوانی کند. از همین قابلیت می‌توان جهت نمایش ساختارهای درختی، مانند مدل‌های خود ارجاع دهنده‌ی EF استفاده کرد.


مدل برنامه، جهت تامین داده‌های خود ارجاع دهنده و درختی

فرض کنید قصد داریم لیستی از کامنت‌های تو در تو را مدل سازی کنیم که در آن هر کامنت، می‌تواند چندین کامنت تا بی‌نهایت سطح تو در تو را داشته باشد:
namespace BlazorTreeView.ViewModels;

public class Comment
{
    public IList<Comment> Comments = new List<Comment>();
    public string? Text { set; get; }
}
برای نمونه بر اساس این مدل، منبع داده‌ی فرضی زیر را تهیه می‌کنیم:
using BlazorTreeView.ViewModels;

namespace BlazorTreeView.Pages;

public partial class TreeView
{
    private IReadOnlyDictionary<string, object> ChildrenHtmlAttributes { get; } =
        new Dictionary<string, object>(StringComparer.Ordinal)
        {
            { "style", "list-style: none;" },
        };

    private IList<Comment> Comments { get; } =
        new List<Comment>
        {
            new()
            {
                Text = "پاسخ یک",
            },
            new()
            {
                Text = "پاسخ دو",
                Comments =
                    new List<Comment>
                    {
                        new()
                        {
                            Text = "پاسخ اول به پاسخ دو",
                            Comments =
                                new List<Comment>
                                {
                                    new()
                                    {
                                        Text = "پاسخی به پاسخ اول پاسخ دو",
                                    },
                                },
                        },
                        new()
                        {
                            Text = "پاسخ دوم به پاسخ دو",
                        },
                    },
            },
            new()
            {
                Text = "پاسخ سوم",
            },
        };
}
این قطعه کد partial class که مربوط به فایل TreeView.razor.cs برنامه‌است، در حقیقت کدهای پشت صحنه‌ی کامپوننت مثال TreeView.razor است که در ادامه آن‌را توسعه خواهیم داد. در نهایت قرار است بتوانیم آن‌را به صورت زیر رندر کنیم:



طراحی کامپوننت DntTreeView

برای اینکه بتوانیم به یک کامپوننت با قابلیت استفاده‌ی مجدد بررسیم، کدهای نمایش اطلاعات تو در تو و درختی را توسط کامپوننت سفارشی DntTreeView پیاده سازی خواهیم کرد. پیشنیازهای آن نیز به صورت زیر است:
- این کامپوننت باید جنریک باشد؛ یعنی باید به صورت زیر شروع شود:
/// <summary>
///   A custom DntTreeView
/// </summary>
public partial class DntTreeView<TRecord>
{
چون باید بتوان یک لیست جنریک <IEnumerable<TRecord را به آن، جهت رندر ارسال کرد و قرار نیست این کامپوننت، تنها به شیء سفارشی Comment مثال جاری ما وابسته باشد. بنابراین اولین خاصیت آن، شیء جنریک Items است که لیست کامنت‌ها/عناصر را دریافت می‌کند:
    /// <summary>
    ///     The treeview's self-referencing items
    /// </summary>
    [Parameter]
    public IEnumerable<TRecord>? Items { set; get; }
- هنگام رندر هر آیتم کامنت باید بتوان یک قالب سفارشی را از کاربر دریافت کرد. نمی‌خواهیم صرفا برای مثال Text شیء Comment فوق را به صورت متنی و ساده نمایش دهیم. می‌خواهیم در حین رندر، کل شیء TRecord جاری را به مصرف کننده ارسال و یک قالب سفارشی را از آن دریافت کنیم. یعنی باید یک RenderFragment جنریک را به صورت زیر نیز داشته باشیم تا مصرف کننده بتواند TRecord در حال رندر را دریافت و قالب Htmlای خودش را بازگشت دهد:
    /// <summary>
    ///     The treeview item's template
    /// </summary>
    [Parameter]
    public RenderFragment<TRecord>? ItemTemplate { set; get; }
- همچنین همیشه باید به فکر عدم وجود اطلاعاتی برای نمایش نیز بود. به همین جهت بهتر است قالب دیگری را نیز از مصرف کننده برای اینکار درخواست کنیم و نحوه‌ی رندر سفارشی این قسمت را نیز به مصرف کننده واگذار کنیم:
    /// <summary>
    ///     The content displayed if the list is empty
    /// </summary>
    [Parameter]
    public RenderFragment? EmptyContentTemplate { set; get; }
- زمانیکه با شیء از پیش تعریف شده‌ی Comment این مثال کار می‌کنیم، کاملا مشخص است که خاصیت Comments آن تو در تو است:
public class Comment
{
    public IList<Comment> Comments = new List<Comment>();
    public string? Text { set; get; }
}
اما زمانیکه با یک کامپوننت جنریک کار می‌کنیم، نیاز است از مصرف کننده، نام این خاصیت تو در تو را به نحو واضحی دریافت کنیم؛ به صورت زیر:
    /// <summary>
    ///     The property which returns the children items
    /// </summary>
    [Parameter]
    public Expression<Func<TRecord, IEnumerable<TRecord>>>? ChildrenSelector { set; get; }
دلیل استفاده از Expression Funcها را در مطلب «static reflection» می‌توانید مطالعه کنید. زمانیکه قرار است از کامپوننت DntTreeView استفاده کنیم، ابتدا نوع جنریک آن‌را مشخص می‌کنیم، سپس لیست اشیاء ارسالی به آن‌را و در ادامه با استفاده از ChildrenSelector به صورت زیر، مشخص می‌کنیم که خاصیت Comments است که به همراه Children می‌باشد و تو در تو است:
        <DntTreeView
            TRecord="Comment"
            Items="Comments"
            ChildrenSelector="m => m.Comments"
و مرسوم است جهت بالابردن کارآیی Expression Funcها، آن‌ها را کامپایل و کش کنیم که نمونه‌ای از روش آن‌را به صورت زیر مشاهده می‌کنید:
public partial class DntTreeView<TRecord>
{
    private Expression? _lastCompiledExpression;
    internal Func<TRecord, IEnumerable<TRecord>>? CompiledChildrenSelector { private set; get; }

    // ...

    protected override void OnParametersSet()
    {
        if (_lastCompiledExpression != ChildrenSelector)
        {
            CompiledChildrenSelector = ChildrenSelector?.Compile();
            _lastCompiledExpression = ChildrenSelector;
        }
    }
}
تا اینجا ساختار کدهای پشت صحنه‌ی DntTreeView.razor.cs مشخص شد. اکنون UI این کامپوننت را به صورت زیر تکمیل می‌کنیم:
@namespace BlazorTreeView.Pages.Components
@typeparam TRecord

@if (Items is null || !Items.Any())
{
    @EmptyContentTemplate
}
else
{
    <CascadingValue Value="this">
        <ul @attributes="AdditionalAttributes">
            @foreach (var item in Items)
            {
                <DntTreeViewChildrenItem TRecord="TRecord" ParentItem="item"/>
            }
        </ul>
    </CascadingValue>
}
در ابتدای کار، اگر آیتمی برای نمایش وجود نداشته باشد، EmptyContentTemplate دریافتی از استفاده کننده را رندر می‌کنیم. در غیراینصورت، حلقه‌ای را بر روی لیست Items ایجاد کرده و آن‌‌ها را یکی نمایش می‌دهیم. این نمایش، نکات زیر را به همراه دارد:
- نمایش توسط کامپوننت دومی به نام DntTreeViewChildrenItem انجام می‌شود که آن‌‌هم جنریک است و شیء item جاری را توسط خاصیت ParentItem دریافت می‌کند.
- در اینجا یک CascadingValue اشاره کننده به شیء this را هم مشاهده می‌کنید. این روش، یکی از روش‌های اجازه دادن دسترسی به خواص و امکانات یک کامپوننت والد، در کامپوننت‌های فرزند است که در ادامه از آن استفاده خواهیم کرد.


تکمیل کامپوننت بازگشتی DntTreeViewChildrenItem.razor

اگر به حلقه‌ی foreach (var item in Items) در کامپوننت DntTreeView.razor دقت کنید، یک سطح را بیشتر پوشش نمی‌دهد؛ اما کامنت‌های ما چندسطحی و تو در تو هستند و عمق آن‌ها هم مشخص نیست. به همین جهت نیاز است به نحوی بتوان یک طراحی recursive و بازگشتی را در کامپوننت‌های Blazor داشت که خوشبختانه این مورد پیش‌بینی شده‌است و هر کامپوننت Blazor، می‌تواند خودش را نیز فراخوانی کند:
@namespace BlazorTreeView.Pages.Components
@typeparam TRecord

<li @attributes="@SafeOwnerTreeView.ChildrenHtmlAttributes" @key="ParentItem?.GetHashCode()">
    @if (SafeOwnerTreeView.ItemTemplate is not null && ParentItem is not null)
    {
        @SafeOwnerTreeView.ItemTemplate(ParentItem)
    }
    @if (Children is not null)
    {
        <ul>
            @foreach (var item in Children)
            {
                <DntTreeViewChildrenItem TRecord="TRecord" ParentItem="item"/>
            }
        </ul>
    }
</li>
این‌ها کدهای DntTreeViewChildrenItem.razor هستند که در آن، ابتدا ItemTemplate دریافتی از والد یا همان DntTreeView.razor رندر می‌شود. سپس به کمک CompiledChildrenSelector ای که عنوان شد، یک شیء Children را تشکیل داده و آن‌را به خودش (فراخوانی مجدد DntTreeViewChildrenItem در اینجا)، ارسال می‌کند. این فراخوانی بازگشتی، سبب رندر تمام سطوح تو در توی شیء جاری می‌شود.

کدهای پشت صحنه‌ی این کامپوننت یعنی فایل DntTreeViewChildrenItem.razor.cs به صورت زیر است:
/// <summary>
///     A custom DntTreeView
/// </summary>
public partial class DntTreeViewChildrenItem<TRecord>
{
    /// <summary>
    ///     Defines the owner of this component.
    /// </summary>
    [CascadingParameter]
    public DntTreeView<TRecord>? OwnerTreeView { get; set; }

    private DntTreeView<TRecord> SafeOwnerTreeView =>
        OwnerTreeView ??
        throw new InvalidOperationException("`DntTreeViewChildrenItem` should be placed inside of a `DntTreeView`.");

    /// <summary>
    ///     Nested parent item to display
    /// </summary>
    [Parameter]
    public TRecord? ParentItem { set; get; }

    private IEnumerable<TRecord>? Children =>
        ParentItem is null || SafeOwnerTreeView.CompiledChildrenSelector is null
            ? null
            : SafeOwnerTreeView.CompiledChildrenSelector(ParentItem);
}
با استفاده از یک پارامتر از نوع CascadingParameter، می‌توان به اطلاعات شیء CascadingValue ای که در کامپوننت والد DntTreeView.razor قرا دادیم، دسترسی پیدا کنیم. سپس یکبار هم بررسی می‌کنیم که آیا نال هست یا خیر. یعنی قرار نیست که این کامپوننت فرزند، درجائی به صورت مستقیم استفاده شود. فقط قرار است داخل کامپوننت والد فراخوانی شود. به همین جهت اگر این CascadingParameter نال بود، یعنی این کامپوننت فرزند، به اشتباه فراخوانی شده و با صدور استثنائی این مساله را گوشزد می‌کنیم. اکنون که به SafeOwnerTreeView یا همان نمونه‌ای از شیء والد دسترسی پیدا کردیم، می‌توانیم پارامتر CompiledChildrenSelector آن‌را نیز فراخوانی کرده و توسط آن، به شیء تو در توی جدیدی در صورت وجود، جهت رندر بازگشتی آن رسید.
یعنی این کامپوننت ابتدا ParentItem، یا اولین سطح ممکن و در دسترس را رندر می‌کند. سپس با استفاده از Expression Func مهیای در کامپوننت والد، شیء فرزند را در صورت وجود یافته و سپس به صورت بازگشتی آن‌را با فراخوانی مجدد خودش ، رندر می‌کند.


روش استفاده از کامپوننت DntTreeView

اکنون که کار توسعه‌ی کامپوننت جنریک DntTreeView پایان یافت، روش استفاده‌ی از آن به صورت زیر است:
<div class="card" dir="rtl">
    <div class="card-header">
        DntTreeView
    </div>
    <div class="card-body">
        <DntTreeView
            TRecord="Comment"
            Items="Comments"
            ChildrenSelector="m => m.Comments"
            style="list-style: none;"
            ChildrenHtmlAttributes="ChildrenHtmlAttributes">
            <ItemTemplate Context="record">
                <div class="card mb-1">
                    <div class="card-body">
                        <span>@record.Text</span>
                    </div>
                </div>
            </ItemTemplate>
            <EmptyContentTemplate>
                <div class="alert alert-warning">
                    There is no item to display!
                </div>
            </EmptyContentTemplate>
        </DntTreeView>
    </div>
</div>
همانطور که مشاهده می‌کنید، چون کامپوننت جنریک است، باید نوع TRecord را که در مثال ما، شیء Comment است، مشخص کرد. سپس لیست نظرات، خاصیت تو در تو، قالب سفارشی نمایش Text نظرات (با توجه به Context دریافتی که امکان دسترسی به شیء جاری در حال رندر را میسر می‌کند) و همچنین قالب سفارشی نبود اطلاعاتی برای نمایش را تعریف می‌کنیم.

کدهای کامل این مطلب را از اینجا می‌توانید دریافت کنید: BlazorTreeView.zip
کامپوننت توسعه یافته‌ی در اینجا در هر دو حالت Blazor WASM و Blazor Server کار می‌کند.
مطالب
ساخت بسته‌های نیوگت مخصوص NET Core.
فایل‌های nuspec مخصوص سایر نگارش‌های دات نت، در NET Core. ندید گرفته شده و پردازش نمی‌شوند. در اینجا نیز تمام تنظیمات تولید بسته‌های نیوگت، در فایل project.json درج می‌شوند که در ادامه آن‌ها را بررسی خواهیم کرد.


فعالسازی تولید خودکار بسته‌های نیوگت در پروژه‌های NET Core.

پس از تهیه‌ی یک کتابخانه‌ی مبتنی بر NET Core.، تنها کاری که در جهت تولید خودکار بسته‌های نیوگت باید انجام شود، افزودن مدخل postcompile ذیل به فایل project.json است:
    "scripts": {
        "postcompile": [
            "dotnet pack --no-build --configuration %compile:Configuration%"
        ]
    }
پس از آن هربار که پروژه کامپایل شود، به صورت خودکار فایل nupkg نهایی در پوشه‌ی bin\Release تشکیل می‌شود.
در این‌حالت اگر فایل nupkg تولیدی را توسط برنامه‌های zip باز کنید، مشاهده خواهید کرد که فایل nuspec خودکاری نیز در آن درج شده‌است؛ اما ... مشخصات ثبت شده‌ی در آن ناقص هستند و شامل مواردی مانند نام پروژه، نام نویسنده، مجوز استفاده‌ی از پروژه، آدرس پروژه و امثال آن‌ها نیستند. در نگارش‌های دیگر دات نت، این مشخصات از فایل nuspec تهیه شده‌ی توسط ما جمع آوری و درج می‌شود. اما در اینجا خیر.


تکمیل فایل project.json برای درج مشخصات پروژه و تکمیل اطلاعات فایل nuspec

هرچند به ظاهر دیگر فایل nuspec دستی تهیه شده در اینجا پردازش نمی‌شود، اما تمام اطلاعات آن‌را در فایل project.json نیز می‌توان درج کرد:
{
    "version": "1.1.1.0",
    "authors": [ "Vahid Nasiri" ],
    "packOptions": {
        "owners": [ "Vahid Nasiri" ],
        "tags": [ "PdfReport", "Excel", "Export", "iTextSharp", "PDF", "Report", "Reporting", "Persian", ".NET Core" ],
        "licenseUrl": "http://www.gnu.org/licenses/old-licenses/lgpl-2.0-standalone.html",
        "projectUrl": "https://github.com/VahidN/iTextSharp.LGPLv2.Core"
    },
    "description": " iTextSharp.LGPLv2.Core  is an unofficial port of the last LGPL version of the iTextSharp (V4.1.6) to .NET Core.",

    "scripts": {
        "postcompile": [
            "dotnet pack --no-build --configuration %compile:Configuration%"
        ]
    }
}
در اینجا یک نمونه از مشخصات فایل project.json ایی را مشاهده می‌کنید که در آن مواردی مانند نویسندگان، برچسب‌هایی که در سایت نیوگت لیست خواهند شد، آدرس مجوز پروژه، آدرس مخزن کد پروژه و توضیحات آن، تکمیل شده‌اند. قسمت postcompile، دقیقا همین اطلاعات را جهت تولید فایل خودکار nuspec نهایی، استفاده می‌کند.


تکمیل تنظیمات Build پروژه

بهتر است کتابخانه‌های خود را در حالت release و همچنین بهینه سازی شده، توزیع کنید. به همین منظور نیاز است مدخل ذیل را نیز به فایل project.json اضافه کرد:
    "configurations": {
        "Release": {
            "buildOptions": {
                "optimize": true,
                "platform": "anycpu"
            }
        }
    },


افزودن مستندات XML ایی کتابخانه

به احتمال زیاد XML-Docهای هر متد (کامنت‌های مخصوص دات نتی هر متد یا خاصیت عمومی) را نیز به کدهای خود افزوده‌اید. برای اینکه فایل XML نهایی آن به صورت خودکار تولید شده و همچنین در بسته‌ی نیوگت نهایی درج شود، نیاز است مدخل xmlDoc را به buildOptions اضافه کنید:
    "buildOptions": {
        "xmlDoc": true
    },
در این حالت هر عنصری با سطح دسترسی public، باید دارای کامنت باشد. اگر می‌خواهید مجبور به انجام اینکار نشوید و کامپایلر اخطار صادر نکند، می‌توانید از اخطار شماره‌ی 1591 صرفنظر کنید:
    "buildOptions": {
        "xmlDoc": true,
        "nowarn": [ "1591" ] // 1591: missing xml comment for publicly visible type or member
    },


برای مطالعه‌ی بیشتر
project.json reference
نظرات مطالب
خواندنی‌های 25 اردیبهشت
سلام.در مورد بلاگر چند سوال داشتم:
1-آیا با ایجاد تغییر در غالب بلاگ، (مثلا اضافه کردن JQuery یا ..) همه اطلاعات sidebar حذف شده و مجبوریم تمام داده‌های وبجتها را دوباره وارد کنیم (مثلا تمام لینکهای ثبت شده و Rssها)
2-در بلاگر میتوان کلمات کلیدی به پستها اضافه کرد؟
3-بخش آخرین نظرات را چگونه اضافه کردید؟
4-در ادیتور بلاگر، میشود عکس را از سایت دیگری اضافه کرد (که آپلودش نکنیم)؟
5-چرا پس از آپلود هر عکس و اضافه کردن آن، عکس به بالای متن منتقل میشود و در صورتی که به میان متن درگ شود، لینکش را از دست میدهد.؟؟!!
تشکر
مطالب
بهینه‌سازی سایت برای شبکه‌های اجتماعی
چند سالی ست که شبکه‌های اجتماعی رشد چشمگیری در دنیای مجازی داشته‌اند و به حرف اول و پیشتاز آن بدل شده‌اند. این شبکه‌ها در همه‌ی زمینه‌ها از معرفی و فروش محصولات گرفته تا معرفی سایت و وبلاگ و ... بکار گرفته می‌شوند و لذا مقوله‌ی بهینه سازی یک وب سایت برای این شبکه‌های اجتماعی امری ناگزیر است. مباحث سئو که پیرامون بهینه سازی یک وب سایت برای موتورهای جستجو می‌باشند، امروزه با پدیده‌ی جدید دیگری آمیخته شده‌اند و به تعاریف قدیمی و معمول، گزینه‌هایی افزوده شده است. از این‌رو شناخت و بکارگیری روش‌های بهینه، برای این مباحث ضروری می‌باشند.
همانطور که می‌دانید هنگامیگه در ادیتور ارسال مطلب یکی از شبکه‌های اجتماعی (فیسبوک ، توییتر ، گوگل پلاس و ...) آدرس سایتی را وارد میکنید، بلافاصله لینک پردازش شده و پیش نمایشی از آن وبسایت در ادیتور نمایش داده می‌شود. برخی پیش نمایش‌ها حاوی عکس، عنوان لینک و خیلی منظم هستند و برخی دیگر فقط نام و عنوان سایت را در برمی‌گیرند.
برای نمونه به تصاویر زیر دقت کنید:

تصویر فوق مربوط به لینک یک سایت خبری در ادیتور فیسبوک می‌باشد. همانطور که می‌بینید عنوان خبر، عکس و توضیح مختصری در مورد خبر، با نظم و ترتیبی خاص نمایش داده شده است.

حال به تصویر زیر که مربوط به لینکی از همین سایت می‌باشد دقت کنید:


همانطور که می‌بینید، تنها لینکی ساده، بدون هیچ پیش نمایشی از وب سایت نشان داده شده است.
دلیل این اتفاق وجود یا عدم وجود متاتگ‌هایی که Social Media Metadata نامیده می‌شوند می‌باشند. چنانچه وب سایتی بوسیله‌ی این متاتگ‌ها برای شبکه‌های اجتماعی بهینه سازی شده باشد، با قرار دادن هر لینکی در ادیتور شبکه‌های اجتماعی، پیش نمایشی خوب از آن مطلب به نمایش گذاشته می‌شود. اهمیت این متاتگ‌ها در سایت‌های خبری، فروشگاه‌ها، سایت‌های آموزشی و ... بسیار بالا می‌باشد تا از مزایای جذب کاربر توسط شبکه‌های اجتماعی بهره‌مند شوند. با این مقدمه می‌رویم به سراغ معرفی و چگونگی بکارگیری این متاتگ ها.

بخش اول: متاتگ‌های موردنیاز برای لینک‌های حاوی مقالات:

ابتدا تگ html خود را به شکل زیر تغییر دهید:
<html itemscope itemtype="http://schema.org/Article">
برای شناخت و اهمیت بکارگیری خصوصیت itemscope میتوانید اینجا را ببینید. این خصوصیت زوج‌های کلید/مقداری را تعریف می‌کند که جزئی از سند می‌باشند. در ادامه itemtype به تشریح یک زوج کلید/مقدار می‌پردازد که به مرورگر می‌گوید کدام آیتم‌ها برای یک مقاله بهینه شده‌اند.
<title>عنوان صفحه ، حداکثر 60 تا 70 کارکتر باشد</title>
<meta name="description" content="شرح صفحه ، حداکثر 150 کارکتر باشد" /> 

<!-- Schema.org markup for Google+ -->
<meta itemprop="name" content="نام یا عنوان صفحه">
<meta itemprop="description" content="شرح صفحه">
<meta itemprop="image" content="نشانی اینترنتی عکسی که در پیشنمایش نشان داده میشود"> 

<!-- Twitter Card data -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:site" content="کپی رایت نام  سایت">
<meta name="twitter:title" content="نام یا عنوان صفحه">
<meta name="twitter:description" content="شرح صفحه">
<meta name="twitter:creator" content="نویسنده">
<!-- Picture size at least  280x150px -->عکس پیشنمایش با ابعاد حداقل   
<meta name="twitter:image:src" content="نشانی اینترنتی عکس مطلب"> 

<!-- Open Graph data -->
<meta property="og:title" content="عنوان صفحه" />
<meta property="og:type" content="article" />
<meta property="og:url" content="نشانی سایت" />
<meta property="og:image" content="نشانی عکس مطلب" />
<meta property="og:description" content="شرح مطلب" />
<meta property="og:site_name" content="نام سایت" />
<meta property="article:published_time" content="تاریخ انتشار" />
<meta property="article:modified_time" content="تاریخ بروزرسانی" />
<meta property="article:section" content="نام بخش محتوی متن مقاله" />
<meta property="article:tag" content="نام تگ محتوی متن مقاله" />
<meta property="fb:admins" content="شناسه عددی کاربری شما در فیسبوک" />

بخش دوم: متاتگ‌های موردنیاز برای لینک‌های حاوی محصولات:
ابتدا تگ html خود را به شکل زیر تغییر دهید:
<html itemscope itemtype="http://schema.org/Product">
و در نهایت متاتگ‌های زیر را به صفحه خود اضافه کنید:
<title>عنوان صفحه</title>
<meta name="description" content="شرح صفحه" />

<!-- Schema.org markup for Google+ -->
<meta itemprop="name" content="عنوان صفحه">
<meta itemprop="description" content="Tشرح صفحه">
<meta itemprop="image" content="نشانی عکس محصول یا کالا">

<!-- Twitter Card data -->
<meta name="twitter:card" content="product">
<meta name="twitter:site" content="کپی رایت سایت">
<meta name="twitter:title" content="عنوان صفحه">
<meta name="twitter:description" content="شرح محصول یا کالا">
<meta name="twitter:creator" content="نویسنده">
<meta name="twitter:image" content="نشانی عکس محصول یا کالا">
<meta name="twitter:data1" content="قیمت محصول یا کالا">
<meta name="twitter:label1" content="Price">
<meta name="twitter:data2" content="رنگ کالا یا محصحول">
<meta name="twitter:label2" content="Color">

<!-- Open Graph data -->
<meta property="og:title" content="عنوان صفحه" />
<meta property="og:type" content="article" />
<meta property="og:url" content="نشانی سایت" />
<meta property="og:image" content="عکس محصول یا کالا" />
<meta property="og:description" content="شرح مصحول" />
<meta property="og:site_name" content="نام سایت" />
<meta property="og:price:amount" content="قیمت محصول یا کالا" />
<meta property="og:price:currency" content="واحد ارزی قیمت" />

در پایان
برای مشاهده لیست کاملی از اسکیما اینجا را ببینید.
og مخفف Open Graph Protocol می‌باشد که می‌توانید مطالب کاملی را در مورد آن اینجا بخوانید.
و برای آشنایی بیشتر با TwitterCard هم این لینک را مشاهده کنید.
مطالب
پشتیبانی از IE در برنامه‌های Angular
اگر یک برنامه‌ی Angular را به صورت پیش‌فرض در IE اجرا کنیم، یک چنین تصویری مشاهده خواهد شد:


برای اجرای برنامه توسط نگارش‌های مختلف IE می‌توانید برنامه‌ی IE Tester را نصب کنید.


مشکل چیست؟

مشکل عدم اجرای برنامه‌های Angular در IE، به قدیمی بودن موتور JavaScript آن بر می‌گردد؛ خصوصا در مورد توابع کار با آرایه‌ها. برای مثال در مورد کار با for..of هیچ نوع پشتیبانی از آن در IE وجود ندارد (و نخواهد داشت؛ با توجه به پایان دوره‌ی پشتیبانی آن):



چگونه پشتیبانی از ویژگی‌های جدید JavaScript را به مرورگر IE اضافه کنیم؟

Angular امکان افزودن کمبودهای موتور JavaScript پیش‌فرض IE را توسط کتابخانه‌ی core-js میسر کرده‌است که اصطلاحا به آن polyfills گفته می‌شود. برای این منظور فایل src\polyfills.ts را گشوده و تغییرات زیر را به آن اعمال کنید:
الف) در این فایل، هرجایی  import // وجود دارد، آن‌را تبدیل به import کنید (تمام importهایی که کامنت شده‌اند را از حالت کامنت خارج کنید).
ب) دو بسته‌ی زیر را نیز باید نصب کنید:
npm install --save classlist.js
npm install --save web-animations-js
البته اگر به کامنت‌های این فایل دقت کنید، نیاز به نصب این بسته‌ها نیز در آن عنوان شده‌است.


نتیجه‌ی نهایی پس از افزودن polyfills مخصوص IE

اکنون اگر مجددا برنامه را کامپایل و اجرا کنید، برنامه‌ی Angular بدون مشکل در IE اجرا خواهد شد:

نظرات مطالب
ساخت ربات تلگرامی با #C
سلام
شما کلا از چند طریق میتوانید  داده‌های خودتون رو به بات ارسال کنید
  1. از طریق تایپ و ارسال عادی متن که سمت سرور می‌توانید متن رو بگیرید و سمت سرور روش شرط بذارید.
  2. از طریق KeyboardButton  که دکمه‌هایی هستند زیر بات میان و می‌توانید با کلیک بر روی هر دکمه، نوشته داخل دکمه رو ارسال کنید که شبیه روش ۱ فقط با دکمه انجام میشه 
  3. ازطریق InlineKeyboardButton که دکمه‌های شیشه‌ای هستند و در محیط چت نمایش داده میشوند که میتوان با کلیک بر روی هر دکمه، مقداری را که پشت پرده ست کردید، بفرستید.
طوری که من سوال شمارو فهمیدم شما با هرکدام از روش‌های بالا داده را به سرور بفرستید داخل سرور باید روی متن رشته‌ای فرستاده شده شرط بذارید و طبق شرطتون داده را مجدد برای کاربر بفرستید.
همچنین میتوانید این مقاله را هم مطالعه نمائید.
مطالب دوره‌ها
به روز رسانی غیرهمزمان قسمتی از صفحه به کمک jQuery در ASP.NET MVC
یک صفحه شلوغ و سنگین را در نظر بگیرید. برای مثال قرار است ابتدا مطلب خاصی در سایت نمایش یابد و سپس ادامه صفحه که شامل انبوهی از لیست نظرات مرتبط با آن مطلب است به صورت غیرهمزمان و Ajax ایی بدون توقف پردازش صفحه، در فرصتی مناسب از سرور دریافت و به صفحه اضافه گردد (به روز رسانی قسمتی از صفحه در فرصت مناسب). در این حالت چون نمایش اولیه صفحه سریع صورت می‌گیرد، کاربر نهایی آنچنان احساس کند بودن بازکردن صفحات سایت را نخواهد داشت. در ادامه نحوه پیاده سازی این روش را به کمک jQuery Ajax بررسی خواهیم کرد.

مدل و کنترلر برنامه

namespace jQueryMvcSample07.Models
{
    public class BlogPost
    {
        public int Id { set; get; }
        public string Title { set; get; }
        public string Body { set; get; }
    }
}

using System.Web.Mvc;
using System.Web.UI;
using jQueryMvcSample07.Models;
using jQueryMvcSample07.Security;

namespace jQueryMvcSample07.Controllers
{
    public class HomeController : Controller
    {
        [HttpGet]
        public ActionResult Index()
        {
            return View(); //نمایش یک منوی ساده در ابتدای کار
        }

        [HttpGet]
        public ActionResult ShowSynchronous()
        {
            var model = getModel();
            return View(model); //نمایش همزمان
        }

        private static BlogPost getModel()
        {
            //شبیه سازی یک عملیات طولانی
            System.Threading.Thread.Sleep(3000);
            var model = new BlogPost
            {
                Title = "عنوان ... ",
                Body = "مطلب... "
            };
            return model;
        }

        [HttpGet]
        public ActionResult ShowAsynchronous()
        {
            return View(); //نمایش ابتدایی صفحه
        }

        [HttpPost]
        [AjaxOnly]
        [OutputCache(Location = OutputCacheLocation.None, NoStore = true)]
        public ActionResult RenderAsynchronous()
        {
            //دریافت اطلاعات به صورت غیرهمزمان
            var model = getModel();
            return PartialView(viewName: "_Post", model: model);
        }
    }
}
مدل برنامه، بیانگر ساختار اطلاعات مطلبی است که قرار است نمایش یابد.
در کنترلر Home، ابتدا اکشن متد Index آن فراخوانی شده و در این حالت دو لینک زیر نمایش داده می‌شوند:
@{
    ViewBag.Title = "Index";
}
<h2>
    نمایش اطلاعات به صورت همزمان و غیرهمزمان</h2>
<ul>
    <li>
        @Html.ActionLink(linkText: "نمایش همزمان", actionName: "ShowSynchronous", controllerName: "Home")
    </li>
    <li>
        @Html.ActionLink(linkText: "نمایش غیر همزمان", actionName: "ShowAsynchronous", controllerName: "Home")
    </li>
</ul>
لینک اول، به اکشن متد ShowSynchronous اشاره می‌کند و لینک دوم به اکشن متد ShowAsynchronous.
در هر دو صفحه نهایتا از یک Partial View به نام _Post.cshtml برای نمایش اطلاعات استفاده خواهد شد:
@model jQueryMvcSample07.Models.BlogPost
<fieldset>
    <legend>@Model.Title</legend>
    @Model.Body
</fieldset>
زمانیکه کاربر بر روی لینک نمایش همزمان کلیک می‌کند، به صفحه زیر هدایت می‌شود:
@model jQueryMvcSample07.Models.BlogPost
@{
    ViewBag.Title = "ShowSynchronous";
}

<h2>نمایش همزمان</h2>
@{ Html.RenderPartial("_Post", Model); }
این صفحه، یک صفحه متداول است و اطلاعات آن دقیقا در زمان نمایش صفحه اخذ شده و چون در اینجا از یک Sleep عمدی برای تولید اطلاعات استفاده گردیده است، نمایش آن حداقل سه ثانیه طول خواهد کشید.
در حالتیکه کاربر بر روی لینک نمایش غیرهمزمان کلیک می‌کند، صفحه زیر را مشاهده خواهد کرد:
@{
    ViewBag.Title = "ShowAsynchronous";
    var loadInfoUrl = Url.Action(actionName: "RenderAsynchronous", controllerName: "Home");
}
<h2>
    نمایش غیر همزمان</h2>
<div id="info" align="center">
</div>
<div id="progress" align="center" style="display: none">
    <br />
    <img src="@Url.Content("~/Content/images/loadingAnimation.gif")" alt="loading..."  />
</div>
@section JavaScript
{
    <script type="text/javascript">
        $(function () {
            $("#progress").css("display", "block");
            $.ajax({
                type: "POST",
                url: '@loadInfoUrl',
                complete: function (xhr, status) {
                    var data = xhr.responseText;
                    if (xhr.status == 403) {
                        window.location = "/login";
                    }
                    else if (status === 'error' || !data || data == "nok") {
                        alert('خطایی رخ داده است');
                    }
                    else {
                        $("#progress").css("display", "none");
                        $("#info").html(data);
                    }
                }
            });
        });
    </script>
}
نمایش ابتدایی این صفحه بسیار سریع است. در ابتدای کار progress bar ایی فعال شده و سپس از طریق jQuery Ajax درخواست دریافت اطلاعات رندر شده اکشن متدی به نام RenderAsynchronous به سرور ارسال می‌شود. چون عملیات Ajax غیرهمزمان است، کاربر نیازی نیست تا رندر شدن کامل صفحه ابتدا صبر کند و سپس کل صفحه به او نمایش داده شود. در اینجا ابتدا صفحه به صورت کامل نمایان شده و سپس درخواستی Ajax ایی به سرور ارسال می‌گردد. در پایان عملیات، Partial View یاد شده رندر گردیده و در div ایی با id مساوی info نمایش داده می‌شود.
به این ترتیب می‌توان حس سریع بودن سایت را زمانیکه قسمتی از صفحه نیاز به زمان بیشتری برای نمایش اطلاعات دارد، به کاربر منتقل کرد.

دریافت پروژه کامل این قسمت
jQueryMvcSample07.zip 
نظرات اشتراک‌ها
خلاصه نکات SEO
روش UrlRewrite ذکر شده در کامنت‌های آن بحث با هر دو حالت وب فرم و MVC سازگار است و نهایتا به یک سایت یکپارچه و یک دست خواهید رسید.