مطالب
ASP.NET MVC #3

تهیه پیش‌نیازهای شروع به کار با ASP.NET MVC

در زمان نگارش این مطلب، نگارش نهایی ASP.NET MVC 3 در دسترس است و همچنین نگارش بتای 4 آن نیز قابل دریافت و نصب می‌باشد. بنابراین فعلا اساس را بر مبنای نگارشی قرار خواهیم داد که در محیط کاری قابل استفاده باشد.
ASP.NET MVC 3 پس از ارائه Visual Studio 2010، منتشر شد و VS.NET به صورت پیش فرض به همراه ASP.NET MVC 2 است. ساده‌ترین روش نصب ASP.NET MVC 3 بر روی VS 2010 استفاده از برنامه رایگانی است به نام Web Platform Installer. این برنامه را از این آدرس می‌توان دریافت کرد: http://microsoft.com/web/downloads
پس از دریافت آن حداقل دو راه برای نصب ASP.NET MVC 3 وجود دارد. یا گزینه‌ی نصب ASP.NET MVC 3 Tools Update را انتخاب کنید و یا سرویس پک یک VS 2010 را از طریق این برنامه یا جداگانه (بسته کامل و مستقل) دریافت و نصب نمائید. VS 2010 SP1 نیز به همراه ASP.NET MVC 3 است؛ همچنین IIS Express را که نسخه ساده شده IIS 7.5 مخصوص توسعه دهنده‌ها است، می‌توان با این نگارش یکپارچه کرد.


بنابراین به صورت خلاصه بهترین کار این است که سرویس پک یک VS 2010 را یکبار نصب نمائید. اگر این نصب از طریق برنامه Web Platform Installer باشد، به صورت خودکار IIS Express را هم انتخاب و نصب خواهد کرد. اگر فقط SP1 را به صورت مستقل دریافت کرده‌اید، حاوی IIS Express نیست و باید جداگانه آن‌را دریافت و نصب نمائید (^). البته نصب IIS Express در اینجا یک گزینه اختیاری است و الزامی نیست.



مروری بر ساختار یک پروژه ASP.NET MVC

پس از نصب پیش نیازها، امکان انتخاب یک پروژه وب ASP.NET MVC 3 در VS 2010 میسر خواهد شد:


در اینجا گزینه‌ی ASP.NET MVC 3 Web Application را انتخاب می‌کنیم. در صفحه بعدی که ظاهر می‌شود:


حالت Internet Application به همراه یک سری مدل و کنترلر از پیش نوشته شده جهت مدیریت ورود به سایت و ثبت نام در سایت است و حالت Empty تنها به همراه ساختار پیش فرض پوشه‌های یک پروژه ASP.NET MVC است.
فعلا جهت توضیحات اولیه بیشتر، گزینه‌ی Internet Application و نوع View Engine را هم ASPX انتخاب می‌کنیم. کار View Engine، رندر یک View به شکل HTML و ارائه نهایی اطلاعات آن به کاربر است. این نوع‌های متفاوت هم فقط در Syntax تفاوت دارند (به آن templating language هم گفته می‌شود). نوع ASPX همان Syntax متداول قدیمی ASP.NET را تداعی می‌کند و نوع Razor به صورت اختصاصی برای ASP.NET MVC تهیه شده است.
باید در نظر داشت که گزینه مرجح از نگارش 3 به بعد، Razor است (البته این هم سلیقه‌ای است. اگر هیچکدام از این دو را هم نخواهید استفاده کنید مشکلی نیست! می‌شود کلا آن را عوض کرد). هدفم هم از انتخاب ASPX نمایش یک سری ریزه کاری است که شاید برای برنامه نویس‌های ASP.NET Web forms جالب باشد. این موارد را در حالت انتخاب Razor به این وضوح مشاهده نخواهید کرد و محیط خیلی ساده شده است.


همانطور که ملاحظه می‌کنید این فریم ورک یک سری پوشه پیش فرض را توصیه می‌کند. بدیهی است که ضرورتی ندارد تا پوشه Models یا پوشه Controllers حتما در همین پروژه قرار داشته باشند؛ چون زمانیکه پروژه کامپایل شد، محل این پوشه بندی‌ها آنچنان اهمیتی ندارد.
نکته جالب در این تصویر، فایل Site.Master است. بله، این فایل شبیه به همان فایل master page موجود در ASP.NET Web form است که قالب کلی سایت را به همراه داشته و سایر صفحات، قالب خود را از آن به ارث می‌برند. حتی تگ runat=server هم به وضوح در این فایل، در چندین جای آن قابل مشاهده است. تنها تفاوت آن نداشتن فایل code behind است. asp:ContentPlaceHolder نیز در آن تعریف شده است. خلاصه این محیط جدید به معنای دور ریختن تمام آنچیزی که در Web forms وجود دارد نیست. برای نمونه اگر فایل ChangePassword.aspx موجود در پوشه Account را باز کنید، باز هم همان asp:Content معروف به همراه تگ runat=server قابل مشاهده است. برای مثال این محتوای صفحه Error.aspx پیش فرض آن است:

<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master"
Inherits="System.Web.Mvc.ViewPage<System.Web.Mvc.HandleErrorInfo>" %>

<asp:Content ID="errorTitle" ContentPlaceHolderID="TitleContent" runat="server">
Error
</asp:Content>

<asp:Content ID="errorContent" ContentPlaceHolderID="MainContent" runat="server">
<h2>
Sorry, an error occurred while processing your request.
</h2>
</asp:Content>

اگر از قسمت Inherits آن صرفنظر کنیم، «هیچ» تفاوتی با ASP.NET Web forms ندارد؛ علت هم به این بر می‌گردد که موتوری که Web forms و MVC از آن استفاده می‌کنند، یکی است. هر دو بر فراز موتور ASP.NET معنا پیدا خواهند کرد.


قرار دادهای پوشه‌های پیش فرض یک پروژه ASP.NET MVC

  • پوشه Controllers حاوی کلاس‌های کنترلری است که درخواست‌های رسیده را مدیریت می‌کنند.
  • پوشه Models حاوی کلاس‌هایی است که اشیاء تجاری و همچنین کار با اطلاعات را تعریف و مدیریت می‌کنند.
  • در پوشه Views، فایل‌های قالب‌های رابط کاربری که مسئول ارائه خروجی به کاربر هستند قرار می‌گیرند. همچنین مطابق قرارداد دیگری، اگر نام کنترلر ما مثلا ProductController باشد (با توجه به اینکه نام کلاس آن هم مطابق قرارداد، مختوم به کلمه Controller است)، فایل‌های Viewهای مرتبط با آن در پوشه Views/Product قرار خواهند گرفت.
  • در پوشه Scripts،‌ فایل‌های جاوا اسکریپت مورد استفاده در سایت قرار خواهند گرفت.
  • پوشه Content محل قرارگیری فایل‌های CSS و تصاویر است.
  • پوشه App_Data جایی است که فایل‌هایی با قابلیت read/write در آن قرار می‌گیرند (و باید دقت داشت که فقط همینجا هم باید قرار گیرند و گرنه این نوشتن‌ها در مکان‌های متفرقه، ممکن است سبب ری استارت شدن برنامه شوند:(^)).

مطالب
نحوه اضافه کردن Auto-Complete به جستجوی لوسین در ASP.NET MVC و Web forms
پیشنیازها:
چگونه با استفاده از لوسین مطالب را ایندکس کنیم؟
چگونه از افزونه jQuery Auto-Complete استفاده کنیم؟
نحوه استفاده صحیح از لوسین در ASP.NET


اگر به جستجوی سایت دقت کرده باشید، قابلیت ارائه پیشنهاداتی به کاربر توسط یک Auto-Complete به آن اضافه شده‌است. در مطلب جاری به بررسی این مورد به همراه دو مثال Web forms و MVC پرداخته خواهد شد.


قسمت عمده مطلب جاری با پیشنیازهای یاد شده فوق یکی است. در اینجا فقط به ذکر تفاوت‌ها بسنده خواهد شد.

الف) دریافت لوسین
از طریق NuGet آخرین نگارش را دریافت و به پروژه خود اضافه کنید. همچنین Lucene.NET Contrib را نیز به همین نحو دریافت نمائید.

ب) ایجاد ایندکس
کدهای این قسمت با مطلب برجسته سازی قسمت‌های جستجو شده، یکی است:
using System.Collections.Generic;
using System.IO;
using Lucene.Net.Analysis.Standard;
using Lucene.Net.Documents;
using Lucene.Net.Index;
using Lucene.Net.Store;
using LuceneSearch.Core.Model;
using LuceneSearch.Core.Utils;

namespace LuceneSearch.Core
{
    public static class CreateIndex
    {
        static readonly Lucene.Net.Util.Version _version = Lucene.Net.Util.Version.LUCENE_30;

        public static Document MapPostToDocument(Post post)
        {
            var postDocument = new Document();
            postDocument.Add(new Field("Id", post.Id.ToString(), Field.Store.YES, Field.Index.NOT_ANALYZED));
            var titleField = new Field("Title", post.Title, Field.Store.YES, Field.Index.ANALYZED, Field.TermVector.WITH_POSITIONS_OFFSETS);
            titleField.Boost = 3;
            postDocument.Add(titleField);
            postDocument.Add(new Field("Body", post.Body.RemoveHtmlTags(), Field.Store.YES, Field.Index.ANALYZED, Field.TermVector.WITH_POSITIONS_OFFSETS));
            return postDocument;
        }

        public static void CreateFullTextIndex(IEnumerable<Post> dataList, string path)
        {
            var directory = FSDirectory.Open(new DirectoryInfo(path));
            var analyzer = new StandardAnalyzer(_version);
            using (var writer = new IndexWriter(directory, analyzer, create: true, mfl: IndexWriter.MaxFieldLength.UNLIMITED))
            {
                foreach (var post in dataList)
                {
                    writer.AddDocument(MapPostToDocument(post));
                }

                writer.Optimize();
                writer.Commit();
                writer.Close();
                directory.Close();
            }
        }
    }
}
تنها تفاوت آن اضافه شدن titleField.Boost = 3 می‌باشد. توسط Boost به لوسین خواهیم گفت که اهمیت عبارات ذکر شده در عناوین مطالب، بیشتر است از اهمیت متون آن‌ها.


ج) تهیه قسمت منبع داده Auto-Complete

namespace LuceneSearch.Core.Model
{
    public class SearchResult
    {
        public int Id { set; get; }
        public string Title { set; get; }
    }
}

using System.Collections.Generic;
using System.IO;
using Lucene.Net.Index;
using Lucene.Net.Search;
using Lucene.Net.Store;
using LuceneSearch.Core.Model;
using LuceneSearch.Core.Utils;

namespace LuceneSearch.Core
{
    public static class AutoComplete
    {
        private static IndexSearcher _searcher;

        /// <summary>
        /// Get terms starting with the given prefix
        /// </summary>
        /// <param name="prefix"></param>
        /// <param name="maxItems"></param>
        /// <returns></returns>
        public static IList<SearchResult> GetTermsScored(string indexPath, string prefix, int maxItems = 10)
        {
            if (_searcher == null)
                _searcher = new IndexSearcher(FSDirectory.Open(new DirectoryInfo(indexPath)), true);

            var resultsList = new List<SearchResult>();
            if (string.IsNullOrWhiteSpace(prefix))
                return resultsList;

            prefix = prefix.ApplyCorrectYeKe();

            var results = _searcher.Search(new PrefixQuery(new Term("Title", prefix)), null, maxItems);
            if (results.TotalHits == 0)
            {
                results = _searcher.Search(new PrefixQuery(new Term("Body", prefix)), null, maxItems);
            }

            foreach (var doc in results.ScoreDocs)
            {
                resultsList.Add(new SearchResult
                {
                    Title = _searcher.Doc(doc.Doc).Get("Title"),
                    Id = int.Parse(_searcher.Doc(doc.Doc).Get("Id"))
                });
            }

            return resultsList;
        }
    }
}
توضیحات:
برای نمایش Auto-Complete نیاز به منبع داده داریم که نحوه ایجاد آن‌را در کدهای فوق ملاحظه می‌کنید. در اینجا توسط جستجوی سریع لوسین و امکانات PrefixQuery آن، به تعدادی مشخص (maxItems)، رکوردهای یافت شده را بازگشت خواهیم داد. خروجی حاصل لیستی است از SearchResultها شامل عنوان مطلب و Id آن. عنوان را به کاربر نمایش خواهیم داد؛ از Id برای هدایت او به مطلبی مشخص استفاده خواهیم کرد.


د) نمایش Auto-Complete در ASP.NET MVC

using System.Text;
using System.Web.Mvc;
using LuceneSearch.Core;
using System.Web;

namespace LuceneSearch.Controllers
{
    public class HomeController : Controller
    {
        static string _indexPath = HttpRuntime.AppDomainAppPath + @"App_Data\idx";

        public ActionResult Index(int? id)
        {
            if (id.HasValue)
            {
                //todo: do something
            }
            return View(); //Show the page
        }

        public virtual ActionResult ScoredTerms(string q)
        {
            if (string.IsNullOrWhiteSpace(q))
                return Content(string.Empty);

            var result = new StringBuilder();
            var items = AutoComplete.GetTermsScored(_indexPath, q);
            foreach (var item in items)
            {
                var postUrl = this.Url.Action(actionName: "Index", controllerName: "Home", routeValues: new { id = item.Id }, protocol: "http");
                result.AppendLine(item.Title + "|" + postUrl);
            }

            return Content(result.ToString());
        }
    }
}

@{
    ViewBag.Title = "جستجو";
    var scoredTermsUrl = Url.Action(actionName: "ScoredTerms", controllerName: "Home");
    var bulletImage = Url.Content("~/Content/Images/bullet_shape.png");
}
<h2>
    جستجو</h2>

<div align="center">
    @Html.TextBox("term", "", htmlAttributes: new { dir = "ltr" })
    <br />
    جهت آزمایش lu را وارد نمائید
</div>

@section scripts
{
    <script type="text/javascript">
        EnableSearchAutocomplete('@scoredTermsUrl', '@bulletImage');
    </script>
}

function EnableSearchAutocomplete(url, img) {
    var formatItem = function (row) {
        if (!row) return "";
        return "<img src='" + img + "' /> " + row[0];
    }

    $(document).ready(function () {
        $("#term").autocomplete(url, {
            dir: 'rtl', minChars: 2, delay: 5,
            mustMatch: false, max: 20, autoFill: false,
            matchContains: false, scroll: false, width: 300,
            formatItem: formatItem
        }).result(function (evt, row, formatted) {
            if (!row) return;
            window.location = row[1];
        });
    });
}
توضیحات:
- ابتدا ارجاعاتی را به jQuery، افزونه Auto-Complete و اسکریپت سفارشی تهیه شده، در فایل layout پروژه تعریف خواهیم کرد.
در اینجا سه قسمت را مشاهده می‌کنید: کدهای کنترلر، View متناظر و اسکریپتی که Auto-Complete را فعال خواهد ساخت.
- قسمت مهم کدهای کنترلر، دو سطر زیر هستند:
result.AppendLine(item.Title + "|" + postUrl);
return Content(result.ToString());
مطابق نیاز افزونه انتخاب شده در مثال جاری، فرمت خروجی مدنظر باید شامل سطرهایی حاوی متن قابل نمایش به همراه یک Id (یا در اینجا یک آدرس مشخص) باشد. البته ذکر این Id اختیاری بوده و در اینجا جهت تکمیل بحث ارائه شده است.
return Content هم سبب بازگشت این اطلاعات به افزونه خواهد شد.
- کدهای View متناظر بسیار ساده هستند. تنها نام TextBox تعریف شده مهم می‌باشد که در متد جاوا اسکریپتی EnableSearchAutocomplete استفاده شده است. به علاوه، نحوه مقدار دهی آدرس دسترسی به اکشن متد ScoredTerms نیز مهم می‌باشد.
- در متد EnableSearchAutocomplete نحوه فراخوانی افزونه autocomplete را ملاحظه می‌کنید.
جهت آن، به راست به چپ تنظیم شده است. با 2 کاراکتر ورودی فعال خواهد شد با وقفه‌ای کوتاه. نیازی نیست تا انتخاب کاربر از لیست ظاهر شده حتما با عبارت جستجو شده صد در صد یکی باشد. حداکثر 20 آیتم در لیست ظاهر خواهند شد. اسکرول بار لیست را حذف کرده‌ایم. عرض آن به 300 تنظیم شده است و نحوه فرمت دهی نمایشی آن‌را نیز ملاحظه می‌کنید. برای این منظور از متد formatItem استفاده شده است. آرایه row در اینجا در برگیرنده اعضای Title و Id ارسالی به افزونه است. اندیس صفر آن به عنوان دریافتی اشاره می‌کند.
همچنین نحوه نشان دادن عکس العمل به عنصر انتخابی را هم ملاحظه می‌کنید (در متد result مقدار دهی شده).  window.location را به عنصر دوم آرایه row هدایت خواهیم کرد. این عنصر دوم مطابق کدهای اکشن متد تهیه شده، به آدرس یک صفحه اشاره می‌کند.


ه) نمایش Auto-Complete در ASP.NET WebForms

قسمت عمده مطالب فوق با وب فرم‌ها نیز یکی است. خصوصا توضیحات مرتبط با متد EnableSearchAutocomplete ذکر شده.
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="LuceneSearch.WebForms.Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width" />
    <title>جستجو</title>
    <link href="Content/Site.css" rel="stylesheet" type="text/css" />
    <script src="Scripts/jquery-1.7.1.min.js" type="text/javascript"></script>
    <script src="Scripts/jquery.autocomplete.js" type="text/javascript"></script>
    <script src="Scripts/custom.js" type="text/javascript"></script>
</head>
<body dir="rtl">
    <h2>
        جستجو</h2>
    <form id="form1" runat="server">
    <div align="center">
        <asp:TextBox runat="server" dir="ltr" ID="term"></asp:TextBox>
        <br />
        جهت آزمایش lu را وارد نمائید
    </div>
    </form>
    <script type="text/javascript">
        EnableSearchAutocomplete('Search.ashx', 'Content/Images/bullet_shape.png');
    </script>
</body>
</html>

using System.Text;
using System.Web;
using LuceneSearch.Core;

namespace LuceneSearch.WebForms
{
    public class Search : IHttpHandler
    {
        static string _indexPath = HttpRuntime.AppDomainAppPath + @"App_Data\idx";

        public void ProcessRequest(HttpContext context)
        {
            string q = context.Request.QueryString["q"];
            if (string.IsNullOrWhiteSpace(q))
            {
                context.Response.Write(string.Empty);
                context.Response.End();
            }

            var result = new StringBuilder();
            var items = AutoComplete.GetTermsScored(_indexPath, q);
            foreach (var item in items)
            {
                var postUrl = "Default.aspx?id=" + item.Id;
                result.AppendLine(item.Title + "|" + postUrl);
            }

            context.Response.ContentType = "text/plain";
            context.Response.Write(result.ToString());
            context.Response.End();
        }

        public bool IsReusable
        { get { return false; } }
    }
}

در اینجا بجای Controller از یک Generic handler استفاده شده است (Search.ashx).
result.AppendLine(item.Title + "|" + postUrl);
context.Response.Write(result.ToString());
در آن، عنوان مطالب یافت شده به همراه یک آدرس مشخص، تهیه و در Response نوشته خواهند شد.


کدهای کامل مثال فوق را از اینجا می‌توانید دریافت کنید:
همچنین باید دقت داشت که پروژه MVC آن از نوع MVC4 است (VS2010) و فرض براین می‌باشد که IIS Express 7.5 را نیز پیشتر نصب کرده‌اید.
کلمه عبور فایل: dotnettips91
 
مطالب
استفاده از EF7 با پایگاه داده SQLite تحت NET Core. به کمک Visual Studio Code
در این مقاله سعی داریم مراحل نوشتن و اجرای یک برنامه‌ی ساده را تحت NET Core. و با بهره گیری از دیتابیس SQLite و EF7، دنبال کنیم. همچنین از آنجایی‌که NET Core. به صورت چندسکویی طراحی شده‌است و تحت لینوکس و مکینتاش هم قابل اجراست، در نتیجه مناسب دیدم که ابزار نوشتن این پروژه‌ی ساده نیز قابلیت چندسکویی داشته و تحت لینوکس و مکینتاش نیز قابل اجرا باشد. در نتیجه به جای Visual Studio در این مقاله از Visual Studio Code استفاده شده است.

ابزارهای پیش نیاز:
  1. Visual Studio Code
  2. .NET Core
در اولین قدم، برنامه‌ی متن باز Visual Studio Code را از اینجا دانلود و نصب کنید. برنامه‌ی Visual Studio Code که در ادامه‌ی فعالیت‌های جدید متن باز مایکروسافت به بازار عرضه شده است، سریع، سبک و کاملا قابل توسعه و سفارشی سازی است و از اکثر زبان‌های معروف پشتیبانی می‌کند.
در قدم بعدی، شما باید NET Core. را از اینجا (64 بیتی) دانلود و نصب کنید.

 .NET Core چیست؟
NET Core. در واقع پیاده سازی بخشی از NET. اصلی است که به صورت متن باز در حال توسعه می‌باشد و بر روی لینوکس و مکینتاش هم قابل اجراست. موتور اجرای دات نت کامل CLR نام دارد و NET Core. نیز دارای موتور اجرایی CoreCLR است و شامل فریمورک CoreFX می‌باشد.

در حال حاضر شما می‌توانید با استفاده از NET Core. برنامه‌های کنسولی و تحت وب با ASP.NET 5 بنویسید و احتمالا در آینده می‌توان امیدوار بود که از ساختارهای پیچیده‌تری مثل WPF نیز پشتیبانی کند.

پس از آنکه NET Core. را دانلود و نصب کردید، جهت شروع پروژه، یک پوشه را در یکی از درایوها ساخته (در این مثال E:\Projects\EF7-SQLite-NETCore) و Command prompt را در آنجا باز کنید. سپس دستورات زیر را به ترتیب اجرا کنید:

dotnet new
dotnet restore
dotnet run

دستور dotnet new یک پروژه‌ی ساده‌ی Hello World را در پوشه‌ی جاری ایجاد می‌کند که حاوی فایل‌های زیر است:
  • NuGet.Config (این فایل، تنظیمات مربوط به نیوگت را جهت کشف و دریافت وابستگی‌های پروژه، شامل می‌شود)
  •  Program.cs (این فایل سی شارپ حاوی کد برنامه است)
  • project.json (این فایل حاوی اطلاعات پلتفرم هدف و لیست وابستگی‌های پروژه است)

دستور dotnet restore بر اساس لیست وابستگی‌ها و پلتفرم هدف، وابستگی‌های لازم را از مخزن نیوگت دریافت می‌کند. (در صورتی که در هنگام اجرای این دستور با خطای NullReferenceException مواجه شدید از دستور dnu restore استفاده کنید. این خطا در گیت هاب در حال بررسی است)

دستور dotnet run هم سورس برنامه را کامپایل و اجرا می‌کند. در صورتی که پیام Hello World را مشاهده کردید، یعنی برنامه‌ی شما تحت NET Core. با موفقیت اجرا شده است.

توسعه‌ی پروژه با Visual Studio Code

در ادامه، قصد داریم پروژه‌ی HelloWorld را تحت Visual Studio Code باز کرده و تغییرات بعدی را در آنجا اعمال کنیم. پس از باز کردن Visual Studio Code از منوی File گزینه‌ی Open Folder را انتخاب کنید و پوشه‌ی حاوی پروژه (EF7-SQLite-NETCore) را انتخاب کنید. اکنون پروژه‌ی شما تحت VS Code باز شده و قابل ویرایش است.

سپس از لیست فایل‌های پروژه، فایل project.json را باز کرده و در بخش "dependencies" یک ردیف را برای EntityFramework.SQLite به صورت زیر اضافه کنید. به محض افزودن این خط در project.json و ذخیره‌ی آن، در صورتیکه قبلا این وابستگی دریافت نشده باشد، Visual Studio Code با نمایش یک هشدار در بالای برنامه به شما امکان دریافت اتوماتیک این وابستگی را می‌دهد. در نتیجه کافیست دکمه‌ی Restore را زده و منتظر شوید تا وابستگی EntityFramework.SQLite از مخزن ناگت دانلود و برای پروژه‌ی شما تنظیم شود.

"EntityFramework.SQLite": "7.0.0-rc1-final"

دریافت اتوماتیک وابستگی‌ها توسط Visual Studio Code


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

اکنون همه‌ی موارد، جهت توسعه‌ی پروژه آماده است. ماوس خود را بر روی ریشه‌ی پروژه در VS Code قرار داده و New Folder را انتخاب کنید و نام Models را برای آن تایپ کنید. این پوشه قرار است مدل کلاس‌های پروژه را شامل شود. در اینجا ما یک مدل به نام Book داریم و نام کانتکست اصلی پروژه را هم LibraryContext گذاشته‌ایم.

بر روی پوشه‌ی Models راست کلیک کرده و گزینه‌ی New File را انتخاب کنید. سپس فایل‌های Book.cs و LibraryContext.cs را ایجاد کرده و کدهای زیر را برای مدل و کانتکست، در درون این دو فایل قرار دهید.

Book.cs

namespace Models
{
    public class Book
    {
        public int ID { get; set; }
        public string Title { get; set; }
        public string Author{get;set;}
        public int PublishYear { get; set; }
    }
}
LibraryContext.cs
using Microsoft.Data.Entity;
using Microsoft.Data.Sqlite;

namespace Models
{
    public class LibraryContext : DbContext
    {
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            var connectionStringBuilder = new SqliteConnectionStringBuilder { DataSource = "test.db" };
            var connectionString = connectionStringBuilder.ToString();
            var connection = new SqliteConnection(connectionString);
            optionsBuilder.UseSqlite(connection);
        }
        public DbSet<Book> Books { get; set; }
    }
}
در فایل Book.cs یک مدل ساده به نام Book داریم که حاوی اطلاعات یک کتاب است. فایل LibraryContext.cs نیز حاوی کلاس LibraryContext است که یک مجموعه از کتاب‌ها را با نام Books نگهداری می‌کند. در متد OnConfiguring تنظیمات لازم را جهت استفاده از دیتابیس SQLite با نام test.db، قرار داده‌ایم که البته این کد را در پروژه‌های کامل می‌توان در خارج از LibraryContext قرار داد تا بتوان مسیر ذخیره سازی دیتابیس را کنترل و قابل تنظیم کرد.

در قدم آخر هم کافیست که فایل Program.cs را تغییر دهید و مقادیری را در دیتابیس ذخیره و بازخوانی کنید.
Program.cs
using System;
using Models;

namespace ConsoleApplication
{
    public class Program
    {
        public static void Main(string[] args)
        {
            Console.WriteLine("EF7 + Sqlite with the taste of .NET Core");

            try
            {
                using (var context = new LibraryContext())
                {
                    context.Database.EnsureCreated();

                    var book1 = new Book()
                    {
                        Title = "Adaptive Code via C#: Agile coding with design patterns and SOLID principles ",
                        Author = "Gary McLean Hall",
                        PublishYear = 2014
                    };

                    var book2 = new Book()
                    {
                        Title = "CLR via C# (4th Edition)",
                        Author = "Jefrey Ritcher",
                        PublishYear = 2012
                    };

                    context.Books.Add(book1);
                    context.Books.Add(book2);

                    context.SaveChanges();

                    ReadData(context);
                }

                Console.WriteLine("Press any key to exit ...");
                Console.ReadKey();
            }
            catch (Exception ex)
            {
                Console.WriteLine($"An exception occured: {ex.Message}\n{ex.StackTrace}");
            }
        }

        private static void ReadData(LibraryContext context)
        {
            Console.WriteLine("Books in database:");
            foreach (var b in context.Books)
            {
                Console.WriteLine($"Book {b.ID}");
                Console.WriteLine($"\tName: {b.Title}");
                Console.WriteLine($"\tAuthor: {b.Author}");
                Console.WriteLine($"\tPublish Year: {b.PublishYear}");
                Console.WriteLine();
            }
        }
    }
}
در فایل Program.cs جهت تست پروژه، از روی کلاس Book دو نمونه ساخته و آن‌ها را به دیتابیس افزوده و ذخیره می‌کنیم و در انتها، اطلاعات تمامی کتاب‌های موجود در دیتابیس را با جزییات نمایش می‌دهیم.

جهت اجرای برنامه کافیست Command prompt را در آدرس پروژه باز کرده و دستور dotnet run را اجرا کنید. پروژه‌ی شما کامپایل و اجرا می‌شود و خروجی مشابه زیر را مشاهده خواهید کرد. اگر برنامه را مجددا اجرا کنید، به جای دو کتاب اطلاعات چهار کتاب نمایش داده خواهد شد؛ چرا که در هر مرحله اطلاعات دو کتاب در دیتابیس درج می‌شود.

.NET Core + EF7 + SQLite

اگر به پوشه‌ی bin که در پوشه‌ی پروژه ایجاد شده است، نگاهی بیندازید، خبری از فایل باینری نیست. چرا که در لحظه‌، تولید و اجرا شده است. جهت build کردن پروژه و تولید فایل باینری کافیست دستور dotnet build را اجرا کنید، تا فایل باینری در پوشه‌ی bin ایجاد شود.

جهت انتشار برنامه می‌توانید دستور dotnet publish را اجرا کنید. این دستور نه تنها برنامه، که تمام وابستگی‌های مورد نیاز آن را برای اجرای در یک پلتفرم خاص تولید می‌کند. برای مثال بعد از اجرای این دستور یک پوشه‌ی win7-x64 حاوی 211 فایل در مجموع تولید شده است که تمامی وابستگی‌های این پروژه را شامل می‌شود.

Publishing .NET Core app

در واقع این پوشه تمام وابستگی‌های مورد نیاز پروژه را همراه خود دارد و در نتیجه جهت اجرای این برنامه برخلاف برنامه‌های معمولی دات نت، دیگر نیازی به نصب هیچ وابستگی مجزایی نیست و حتی پروژه‌های نوشته شده تحت NET Core. را می‌توانید در سیستم‌های عامل‌های دیگری مثل لینوکس و مکینتاش و یا  Windows IoT بر روی سخت افزار Raspberry Pi 2 هم اجرا کنید.

جهت مطالعه‌ی بیشتر:
نظرات مطالب
چگونگی گزارشگیری از Business Objects مانند List توسط StimulSoft
روشی که خود من در برنامه استفاده می‌کنم این است که تمام فایل‌های mrt گزارشات را در یک پوشه قرار داده‌ام و یک viewer برای تمام پروژه دارم که با ارسال ReportId - شناسه گزارش مورد نظر- به آن صفحه، نوع گزارش، فایل mrt آن و دیتای آنرا مشخص کرده و گزارش را نمایش می‌دهم.
 روش شما شاید در تعداد گزارشات محدود براحتی قابل مدیریت باشد ولی زمانی که تعداد گزارشات بیشتر شود، مدیریت آن سخت خواهد شد؛ به ویژه که بخواهید گزارش داینامیک تولید کنید.
پ.ن: در این سایت برای تشکر و ابراز علاقه‌مندی نسبت به مطلب یا پاسخی از سیستم امتیازدهی استفاده می‌شود؛ می‌توانید از آن استفاده کنید.
نظرات مطالب
آشنایی با SASS یا Syntactically Awesome Stylesheets و روش نصب و استفاده از آن
- نگارش‌های اخیر VS.NET به صورت توکار LESS را پشتیبانی می‌کنند (از آپدیت دوم VS 2012 به بعد).
- برای SASS فعلا Web Workbench هست. افزونه معروف Web essentials هم به زودی ...
نظرات مطالب
BloggerToCHM
سلام،
برنامه زمانیکه فایل chm رو درست کرد سعی میکنه اون رو نمایش بده. این فایل chm داخل یک فولدر کنار فایل exe درست میشه. لطفا بررسی کنید آیا فایل موجود است و درست شده و همچنین این مسیر به چه صورتی است؟
مثلا فاصله دارد یا از حروف فارسی درست شده یا ...؟
مطالب
افزونه CodeRush 9.2 و ایجاد مشکل برای IDE

نگارش جدید افزونه‌های DevExpress این بار واقعا سنگ تمام گذاشته‌اند و با قابلیت‌های آنالیز کد افزونه‌ی ReSharper قابل مقایسه بوده و یا حتی در بعضی از موارد هم بهتر از آن‌ها هستند:



برای مثال مطابق شکل فوق تنها کافی است یک شیء از نوع disposable داشته باشید که در کد شما به صورت صریح dispose نشده باشد. بلافاصله آن‌را تشخیص داده و راه علاج را نیز توصیه می‌کند.

اما پس از نصب آن، دیگر یک سری از پروژه‌های جدید در IDE قابل ایجاد شدن نیستند (حداقل برای من به این صورت بود). پیغام خطایی هم که از طرف VS.Net صادر می‌شود به صورت زیر است:

The project type is not supported by this installation?

پس از کمی جستجو مشخص شد که روش رفع این مشکل به صورت زیر است:

devenv /setup
regsvr32.exe "%vs90comntools%\..\IDE\projectaggregator.dll"
devenv /resetskippkgs

سه سطر فوق را در خط فرمان صادر کرده و مدتی صبر کنید تا عملیات به پایان رسد. پس از آن مشکل برطرف خواهد شد.

مطالب
کار با Docker بر روی ویندوز - قسمت هفتم - مدیریت اجرای چندین کانتینر به هم وابسته
تا اینجا نحوه‌ی اجرای برنامه‌ها، وب سرورها و حتی بانک‌های اطلاعاتی را توسط داکر بررسی کردیم. در این قسمت می‌خواهیم یک برنامه و بانک اطلاعاتی مخصوص آن‌را داخل یک کانتینر اجرا کنیم و برای این منظور از ابزار ساده کننده‌ی docker-compose استفاده خواهیم کرد.


docker-compose چیست؟

فرض کنید برنامه‌ی ما، از یک قسمت منطق خود برنامه و قسمت دیگر بانک اطلاعاتی آن تشکیل شده‌است. در این حالت برای توزیع آن توسط کانتینرها، نیاز به دو کانتینر مجزا خواهد بود؛ یکی برای برنامه و دیگری برای بانک اطلاعاتی:
dockerrun --name db`
-d `
-p 3306:3306 `
-e MYSQL_ROOT_PASSWORD=my-secret-pw `
-v db:/var/lib/mysql`
mysql

dockerinspect db # extract ipaddress

dockerrun --name web `
-d `
-p 8080:80 `
-e MY_DB_PORT=3306 `
-e MY_DB_HOST=? `
-v /my/php/app:/usr/share/nginx/html `
nginx
تمام این دستورات را به همراه یک ` نیز مشاهده می‌کنید. این روشی است که از آن برای چندسطری کردن دستورات در PowerShell استفاده می‌شود.
- دستور اول مطابق توضیحات قسمت قبل، یک بانک اطلاعاتی MySQL را در پس زمینه، با نام db که در آن، پورت 3306 میزبان به پورت 3306 کانتینر نگاشت شده‌است و همچنین بانک اطلاعاتی آن در یک volume نامدار به نام db با مسیر نگاشت شده‌ی به /var/lib/mysql/ داخل کانتینر ایجاد می‌شود، اجرا می‌کند.
- دستور دوم کار استخراج اطلاعات این کانتینر را انجام می‌دهد که شامل آدرس IP آن نیز می‌باشد. از این IP در برنامه‌ی وب استفاده خواهیم کرد.
- دستور سوم مطابق توضیحات قسمت پنجم، یک وب سرور nginx را برای هاست یک برنامه‌ی PHP که در آن پورت 8080 میزبان به پورت 80 کانتینر نگاشت شده‌است و همچنین فایل‌های آن از مسیر /my/php/app/ میزبان به مسیر /usr/share/nginx/html/ داخل کانتینر نگاشت و تامین می‌شوند، اجرا می‌کند. در اینجا از پارامتر e برای تعریف یک سری متغیر محیطی مانند شماره‌ی پورت و IP کانتینر اجرا کننده‌ی mysql، استفاده شده‌است.

در این مثال دو کانتینر به هم وابسته را اجرا کرده‌ایم و برای اجرای کانتینر دوم، نیاز است حداقل IP کانتینر اول را دانست و در قسمت MY_DB_HOST مقدار دهی کرد. روش دیگری نیز برای مدیریت ساده‌تر اجرای چندین کانتینر به هم وابسته توسط ابزاری به نام docker-compose وجود دارد. اگر از Dockerfile (که آن‌را در قسمت پنجم معرفی کردیم) جهت ایجاد Imageهای سفارشی بکار می‌رود، فایل docker-compose.yml، کار خودکار سازی ایجاد و اجرای چندین کانتینر را انجام می‌دهد که با قالب YAML تعریف می‌شود:
version: '2'
services:
    db:
         image: mysql
         ports:
             -3306:3306
         environment:
             -MYSQL_ROOT_PASSWORD=my-secret-pw
         volumes:
             -db:/var/lib/mysql

    web:
         image: nginx
         ports:
             -8080:80
         environment:
             -MY_DB_PORT=3306
             -MY_DB_HOST=db
         volumes:
             -/my/php/app:/usr/share/nginx/html
همانطور که مشاهده می‌کنید، هرچند قالب آن اندکی متفاوت شده‌است، اما در اصل با دستورات docker ای که در ابتدا معرفی کردیم، تفاوتی ندارد. محتوای فوق را در یک فایل متنی ویژه به نام docker-compose.yml ذخیره و آن‌را به ابزار خط فرمان دیگری به نام docker-compose معرفی می‌کنیم.
در ابتدای این فایل، شماره نگارش قالب YAML مورد استفاده، مشخص شده‌است. در این نگارش، به کانتینرها، services گفته می‌شود که در اینجا دو سرویس db و web را مشاهده می‌کنید. در فایل‌های yml، فضاهای خالی و indentations مهم هستند و بر این اساس است که کانتینرها و سپس مشخصات این کانتینرها، تمیز داده می‌شوند.


راه اندازی TeamCity به کمک فایل docker-compose.yml آن

در اینجا محتویات فایل docker-compose.yml مخصوص راه اندازی TeamCity را مشاهده می‌کنید که از سه کانتینر تشکیل شده‌است و از بانک اطلاعاتی postgres استفاده می‌کند:
version: '2'
services:
    teamcity:
        image: sjoerdmulder/teamcity
        ports:
            - 8111:8111
    teamcity-agent:
        image: sjoerdmulder/teamcity-agent
        environment:
            - TEAMCITY_SERVER=http://teamcity:8111
    postgres:
        image: postgres
        environment:
            - POSTGRES_DB=teamcity
در این تنظیمات، پورت 8111 میزبان به پورت 8111 کانتینر teamcity نگاشت شده‌است. در ادامه teamcity-agent نیاز به IP این کانتینر را دارد (یک build-server است). زمانیکه چندین کانتینر را توسط فایل docker-compose.yml راه اندازی می‌کنیم، داکر یک شبکه‌ی ایزوله (private network) را نیز برای اینکار مهیا می‌کند. داخل این شبکه‌ی ایزوله، یک DNS سرور توکار نیز وجود دارد که امکان دسترسی به کانتینرهای مختلف را از طریق نام کانتینرهای آن‌ها میسر می‌کند. به همین جهت است که مقدار TEAMCITY_SERVER، به http://teamcity:8111 تنظیم شده‌است و دقیقا از نام کانتینر teamcity برای یافتن IP آن استفاده می‌کند. همچنین باید دقت داشت در این آدرس، عدد 8111 به پورت داخل کانتینر teamcity اشاره می‌کند و نه به پورت میزبان. کانتینرها از طریق آدرس IP و پورت خودشان با هم در تماس هستند. پورت میزبان 8111، صرفا جهت فراخوانی teamcity از طریق سیستم میزبان مشخص شده‌است.


در ادامه برای کار با آن، ابتدا این محتویات را به صورت یک فایل متنی docker-compose.yml ذخیره کنید. سپس از طریق خط فرمان به پوشه‌ی آن وارد شده و دستور docker-compose up را صادر کنید. docker-compose یکی دیگر از ابزارهای خط فرمان نصب شده‌ی به همراه داکر است و پارامتر up آن کار راه اندازی و اجرای کانتینرهای ذکر شده‌ی در فایل yml موجود را انجام می‌دهد. نام پوشه‌ای که این فایل در آن قرار دارد، به عنوان نام پروژه‌ی مشترک بین این کانتینرها در گزارشات آن مورد استفاده قرار می‌گیرد.
پس از صدور این فرمان، ابتدا تمام imageهای ذکر شده‌ی در فایل yml دریافت می‌شوند (سه image در اینجا) و هر سه کانتینر راه اندازی می‌شوند. اکنون می‌توان در سیستم میزبان به آدرس http://localhost:8111 مراجعه کرد و از برنامه‌ی teamcity استفاده نمود. البته صفحه‌ی ابتدایی آن کار تنظیمات بانک اطلاعاتی آن‌را انجام می‌دهد و جائیکه در مورد database type سؤال می‌پرسد می‌توان postgres را انتخاب کرد و سپس در ذیل آن مقدار database host را نیز postgres وارد می‌کنیم. علت آن‌را نیز پیشتر توضیح دادیم. postgres در اینجا نام کانتینر نیز هست و ذکر نام آن، با ذکر IP مرتبط با آن کانتینر، یکی است. نام بانک اطلاعاتی را teamcity وارد کنید (مطابق تنظیمات فایل yml فوق) و نام کاربری آن نیز postgres است؛ بدون کلمه‌ی عبور. البته می‌شد در فایل yml فوق، متغیر محیطی POSTGRES_PASSWORD=xyz را نیز تنظیم کرد و سپس از آن در اینجا استفاده نمود.


docker-compose و ایجاد شبکه‌های ایزوله

توسط دستور docker network ls می‌توان لیست شبکه‌های مجازی ایجاد شده‌ی توسط docker را مشاهده کرد (و همچنین سایر network adapters موجود). اگر این دستور را اجرا کنید، کارت شبکه‌ی مجازی متناظر با شبکه‌ی خصوصی teamcity_default را که پیشتر در مورد آن توضیح داده شد، می‌توانید مشاهده کنید. این teamcity در اینجا همان نام پروژه و یا در اصل نام پوشه‌ای است که فایل docker-compose را از آنجا اجرا کردیم.
برای دریافت اطلاعات بیشتری در مورد این کارت شبکه‌ی به خصوص، می‌توان دستور docker network inspect teamcity_default را صادر کرد. یکی از قسمت‌های خروجی این گزارش، لیست کانتینرهایی است که هم اکنون به این شبکه متصل هستند؛ که در اینجا teamcity و بانک اطلاعاتی آن است.
مزیت ایجاد یک شبکه‌ی خصوصی مخصوص کانتینرهای به هم پیوسته، علاوه بر سادگی تشکیل فایل docker-compose آن‌ها با اشاره‌ی به نام کانتینرها، بجای ذکر مستقیم آدرس IP هر کدام، ایزوله ساختن این شبکه، از شبکه‌ی پیش‌فرض docker و بالا بردن میزان امنیت سایر کانتینرهایی است که هم اکنون از آن شبکه استفاده می‌کنند.


docker-compose و ایجاد DNS Server توکار

همانطور که عنوان شد، در این شبکه‌ی خصوصی ویژه‌ی کانتینرهای به هم پیوسته که توسط docker-compse اجرا و مدیریت شده‌اند، می‌توان از نام containerها بجای آدرس IP آن‌ها استفاده کرد و این مورد با وجود یک DNS Server توکار در این شبکه میسر شده‌است. برای آزمایش بیشتر این قابلیت، ابتدا دستور docker ps را صادر می‌کنیم تا نام کانتینرهای در حال اجرا را بدست بیاوریم. سپس سعی می‌کنیم پروسه‌ی bash shell داخل کانتینر بانک اطلاعاتی را اجرا کنیم:
docker ps
docker exec -it teamcity_postgres_1 bash
#exit
استفاده از docker exec یک روش است برای دسترسی به shell این کانتینر در حال اجرا؛ روش دیگر، استفاده از خود docker-compse می‌باشد که در این حالت، کار با آن ساده‌تر است:
 docker-compose exec postgres bash
در اینجا نیازی به ذکر سوئیچ it نیست؛ چون مقدار پیش‌فرض آن است و همچنین دیگر نیازی به اجرای docker ps برای یافتن نام کانتینری هم نیست و مستقیما می‌توان از نام‌های سرویس‌هایی که در فایل docker-compose.yml تعریف شده‌اند، استفاده کرد.
پس از دسترسی به شل، دستور زیر را اجرا کنید:
#ping teamcity
#exit
مشاهده خواهید کرد که این کانتینر می‌تواند کانتینر دیگری را صرفا با ذکر نام آن، ping کند.

یک نکته: اگر بخواهیم از وضعیت بانک‌های اطلاعاتی postgres توسط برنامه‌ی psql آن گزارش بگیریم نیز روش اجرای آن به همین صورت است:
docker-compose exec postgres psql -U postgres
postgres=#\l
postgres=#\q


اتصال یک کانتینر خارج از شبکه‌ی مجازی ایجاد شده‌ی توسط docker-compose به آن

فرض کنید می‌خواهید کانتینر کم حجم لینوکس alpine را اجرا کنید و توسط آن به شبکه‌ی مجازی ایجاد شده‌ی توسط docker-compose متصل شوید. روش آن به صورت زیر است:
 docker run --name apline -it --rm --net teamcity_default alpine sh
در اینجا توسط سوئیچ net، می‌توان نام شبکه‌ی مجازی را که نیاز است به آن متصل شویم، ذکر کرد. نام teamcity_default نیز از طریق اجرای دستور docker netwrok ls بدست آمده‌است.
این دستور، کانتینر لینوکس alpine را در حالت interactive جهت اجرای shell آن، راه اندازی می‌کند. سپس به شبکه‌ی مجازی teamcity_default متصل می‌شود. برای آزمایش این اتصال، در این shell راه اندازی شده، دستور ping teamcity را می‌توان صادر کرد. همچنین از داخل کانتینر teamcity نیز می‌توان این کانتینر را با نام آن ping کرد.


راه اندازی مجدد کانتینرها توسط docker-compose

اگر دستور docker-compose ps را دقیقا در پوشه‌ای که فایل yml آن قرار دارد اجرا کنیم، می‌توان گزارشی را صرفا از وضعیت کانتینرهای مرتبط با این فایل yml بدست آورد. دستور docker ps، لیست وضعیت تمام کانتینرهای در حال اجرای موجود را بر می‌گرداند. اکنون فرض کنید یکی از کانتینرهای اجرای شده‌ی توسط docker-compose، دیگر در حال اجرا نیست. برای راه اندازی مجدد آن می‌توان از دستور docker-compose start teamcity-agent استفاده کرد. همچنین دستور docker-compose logs teamcity-agent، لیست آخرین لاگ‌های مرتبط با یک کانتینر را بر می‌گرداند که می‌تواند برای رفع اشکال بسیار مفید باشد.


حذف کانتینرهای به هم پیوسته‌ی ایجاد شده‌ی توسط docker-compose

در ذیل ابتدا یک سری دستور را جهت مشاهده‌ی وضعیت سیستم مشاهده می‌کنید. سپس دستور docker-compose stop، کار متوقف کردن کانتینرهای مرتبط با فایل yml آن‌را انجام می‌دهد. دستور docker-compose rm -v، علاوه بر حذف این کانتینرها، volumeهای بانک‌های اطلاعاتی مرتبط را نیز حذف می‌کند. در آخر دستور docker-compose down، شبکه‌ی مجازی مرتبط را نیز حذف خواهد کرد. سپس مجددا از وضعیت سیستم گزارش گیری شده‌است.
 docker ps
docker-compose ps
docker volume ls
docker network ls

docker-compose stop
docker-compose rm -v
docker-compose down

docker ps -a
docker volume ls
docker network ls


اجرای پروژه‌ی ASP.NET Core Music Store توسط docker-compose

پروژه‌ی معروف Music Store مایکروسافت را به همراه فایل docker-compose.windows.yml آن، در اینجا می‌توانید مشاهده کنید. محتوای این فایل نیز به صورت زیر است:
version: '3'
services:
  db:
    image: microsoft/mssql-server-windows-developer
    environment:
      sa_password: "Password1"
      ACCEPT_EULA: "Y"
    ports:
      - "1433:1433" # REMARK: This is currently required, needs investigation
    healthcheck:
      test: [ "CMD", "sqlcmd", "-U", "sa", "-P", "Password1", "-Q", "select 1" ]
      interval: 1s
      retries: 30
web:
    build:
      context: .
      dockerfile: Dockerfile.windows
    environment:
      - "Data:DefaultConnection:ConnectionString=Server=db,1433;Database=MusicStore;User Id=sa;Password=Password1;MultipleActiveResultSets=True"
    depends_on:
      - db
    ports:
      - "5000:5000"
در اینجا تعریف دو کانتینر را مشاهده می‌کنید:
- کانتینر db که بر اساس image مخصوص mssql-server-windows-developer راه اندازی می‌شود. تنظیمات آن نیز بسیار شبیه به مطلب «کار با Docker بر روی ویندوز - قسمت ششم - کار با بانک‌های اطلاعاتی درون Containerها» است که پیشتر در مورد آن بحث کردیم.
- کانتینر web آن که از فایل Dockerfile.windows برای build سپس publish و در آخر run خودکار این برنامه‌ی ASP.NET Core، کمک می‌گیرد. در اینجا context به پوشه‌ی جاری اشاره می‌کند. در قسمت تنظیمات بانک اطلاعاتی آن، استفاده‌ی از نام کانتینر db را در قسمت رشته‌ی اتصالی مشاهده می‌کنید. قسمت depends_on آن ترتیب اجرای این کانتینرها را مشخص می‌کند. یعنی ابتدا باید کانتینر db اجرا شود و سپس web.
محتوای فایل Dockerfile.windows آن نیز به صورت زیر است که بر اساس دستورات NET Core CLI. تهیه شده‌است:
FROM microsoft/dotnet-nightly:2.0-sdk-nanoserver
SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]
ENV NUGET_XMLDOC_MODE skip
ENV DOTNET_SKIP_FIRST_TIME_EXPERIENCE 1
RUN New-Item -Path \MusicStore\samples\MusicStore -Type Directory
WORKDIR app
ADD samples/MusicStore/MusicStore.csproj samples/MusicStore/MusicStore.csproj
ADD build/dependencies.props build/dependencies.props
ADD NuGet.config .
RUN dotnet restore --runtime win10-x64 .\samples\MusicStore
ADD samples samples
RUN dotnet publish --output /out --configuration Release --framework netcoreapp2.0 --runtime win10-x64 .\samples\MusicStore
FROM microsoft/dotnet-nightly:2.0-runtime-nanoserver
WORKDIR /app
COPY --from=0 /out .
EXPOSE 5000
ENV ASPNETCORE_URLS http://0.0.0.0:5000
CMD dotnet musicstore.dll
روش اجرای آن‌را نیز به صورت زیر بیان کرده‌است:
docker-compose -f .\docker-compose.windows.yml build
docker-compose -f .\docker-compose.windows.yml up
البته باید دقت داشت که اجرای فرمان up، به صورت خودکار کار build را هم انجام می‌دهد. پس از اجرای آن، برنامه را بر روی پورت 5000 می‌توانید مشاهده کنید.
نظرات مطالب
Build Events
با سلام 
در پاسخ به مشکل شما چند نکته باید اشاره بشه. 

نکته اول: ماکروی TargetFileName فقط اسم فایل خروجی پروژه رو برمی‌گردونه، درصورتیکه برای کارکردن دستور فوق مسیر کامل فایل نیازه. چون برنامه editbin.exe درون مسیر خروجی پروژه شما اجرا نمی‌شه. شما می‌تونین از ماکروی TargetPath استفاده کنید که مسیر کامل فایل خروجی پروژه رو برمی‌گردونه.

نکته دوم: کد خطای 9009 مربوط به پیدا نکردن فایل هست. البته فایلی که در اینجا پیدا نشده خروجی پروژه شما نیست بلکه خود ابزار editbin هستش. مسیر درستش در سیستم 32 بیتی برای ویژوال استودیو 2010 اینه:
C:\Program Files\Microsoft Visual Studio 10.0\VC\bin\editbin.exe
اما چون این مسیرها معمولا حاوی فاصله هستند نیاز به استفاده از دابل کوتیشن در ابتدا و انتها وجود داره. بنابراین دستور کامل باید به صورت زیر باشه:
"C:\Program Files\Microsoft Visual Studio 10.0\VC\bin\editbin.exe" /STACK:1000000 "$(TargetPath)"
اما با اجرای دستور فوق باز هم خطایی صادر میشه که کمی خطرناکتر از قبلیه. و اما دلیلش:

نکته سوم: متن زیر از msdn گرفته شده:
You can start this tool only from the Visual Studio command prompt. You cannot start it from a system command prompt or from Windows Explorer.
البته منظور دقیق‌تر این جمله اینه که ابزار editbin نیاز به یکسری تنظیمات و متغیرهای ازپیش تعیین شده داره که در Visual Studio command prompt انجام شده. اما نگران نباشید برای تنظیم این تنظیمات و تبدیل خط فرمان Build Events در ویژوال استودیو به یک Visual Studio command prompt کافیه که خط زیر رو در ابتدای مجموعه دستورات build events خودتون قرار بدین:
call "$(DevEnvDir)..\Tools\vsvars32.bat"
این بچ فایل حاوی دستوراتی نسبتا مفصل برای تنظیم تنظیمات موردنیاز است. درواقع با اجرای این بچ فایل هر خط فرمانی تقریبا تبدیل به Visual Studio command prompt خواهد شد. با توجه به ماکروی (DevEnvDir)$ مسیر کامل این فایل در سیستم 32 بیتی و برای ویژوال استودیوی 2010 به صورت زیر است:
C:\Program Files\Microsoft Visual Studio 10.0\Common7\Tools\vsvars32.bat
بنابراین برای کار کردن دستور موردنظر شما کافیه که این دو دستور به صورت زیر در Post Build Event اضافه بشه:
call "$(DevEnvDir)..\Tools\vsvars32.bat"
"C:\Program Files\Microsoft Visual Studio 10.0\VC\bin\editbin.exe" /STACK:1000000 "$(TargetPath)"

نکته چهارم: با توجه به اشاره‌ای که در نکته قبلی شد ("با اجرای این فایل هر خط فرمانی تقریبا تبدیل به Visual Studio command prompt خواهد شد.") بنابراین دستور فوق را میتوان به صورت زیر خلاصه کرد:
call "$(DevEnvDir)..\Tools\vsvars32.bat"
"editbin.exe" /STACK:1000000 "$(TargetPath)"

موفق باشید.
نظرات مطالب
ارتقاء به ASP.NET Core 2.0 - معرفی بسته‌ی Microsoft.AspNetCore.All
استفاده از کامپایلر 1.1 برای پروژه‌های 1.1

اگر NET Core SDK. جدید را نصب کنید، سیستم build پروژه، به صورت خودکار از آخرین نگارش نصب شده استفاده می‌کند (یعنی نگارش 2). اگر می‌خواهید این مورد را بازنویسی کنید، نیاز است فایل ویژه‌ای را به نام global.json با محتوای ذیل به ریشه‌ی solution در بالاترین سطح ممکن اضافه کنید:
{ 
  "sdk": { 
    "version": "1.0.4" 
  } 
}
در اینجا عدد 1.0.4 شماره نگارش SDK مدنظر است. این شماره‌ها را در مسیر C:\Program Files\dotnet\sdk می‌توانید مشاهده کنید و بر این اساس باید تصمیم گیری کنید (بر اساس شماره‌های موجود و نصب شده‌ی بر روی سیستم شما).
و همچنین اگر فایل global.json را اضافه کرده‌اید، حین ارتقاء پروژه حتما باید شماره SDK موجود در آن‌را به صورت دستی اصلاح کنید؛ وگرنه پروژه build نخواهد شد. عدم وجود این فایل به معنای استفاده‌ی از آخرین شماره نگارش SDK موجود است.