مطالب
نمایش رکوردها به ترتیب اولویت به کمک jQuery UI sortable در ASP.NET MVC

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

از توضیحاتی که قبلا  دادم مشخص است که این کار احتمالا در ASP.NET WebForm  کار سختی نیست ولی این کار باید در MVC  از ابتدا طراحی شود.

طرح سوال : یک سری رکورد از یک Table داریم که می‌خواهیم به ترتیب وارد شدن رکورد‌ها نباشد و  ترتیبی که ما می‌خواهیم نمایش داده شود.

پاسخ کوتاه : خب باید ابتدا یک فیلد (برای اولویت بندی)  به Table  اضافه کنیم  بعد اون فیلد رو بنا به ترتیبی که دوست داریم رکورد‌ها نمایش داده شود پر کنیم (Sort  می کنیم ) و در آخر هم هنگام نمایش در View رکورد‌ها را بر اساس این فیلد نمایش می‌دهیم.

(این پست هم در ادامه پست قبلی در همان پروژه است و از همان Table  ها استفاده شده است)

اضافه کردن فیلد :

ابتدا یک فیلد به Table  مورد نظر اضافه می‌کنیم. من اسم این فیلد رو Priority گذاشتم. Table  من چنین وضعیتی دارد.

افزودن فایل‌های jQuery UI :

در این مرحله شما نیاز دارید فایل‌های مورد نیاز برای Sort  کردن رکورد‌ها را اضافه کنید. شما می‌توانید فقط فایل‌های مربوط به Sortable  را به صفحه خودتان اضافه کنید و یا مثل من فایل هایی که حاوی تمام قسمت‌های jQuery UI  هست را اضافه کنید.

من برای این کار از Section  استفاده کردم ، ابتدا در Head  فایل Layout  دو Section  تعریف کردم برای CSS  و JavaScript . و فایل‌های مربوط به Sort کردن را در صفحه ای که باید عمل Sort انجام بشود در  این Section ها قرار دادم.

فایل Layout

<head>
    <meta charset="utf-8" />
    @RenderSection("meta", false)
    <title>@ViewBag.Title</title>
    <link href="@Url.Content("~/Content/~Site.css")" rel="stylesheet" type="text/css" />
    <link href="@Url.Content("~/Content/redactor/css/redactor.css")" rel="stylesheet" type="text/css" />
    <link href="@Url.Content("~/Content/css/bootstrap-rtl.min.css")" rel="stylesheet" type="text/css" />
    @RenderSection("css", false)
    <script src="@Url.Content("~/Scripts/jquery-1.8.2.min.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/modernizr-1.7.min.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Content/js/bootstrap-rtl.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Content/redactor/redactor.min.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")" type="text/javascript"></script>
    @RenderSection("js", false)

</head>

فایل Index.chtml  در پوشه کنترلر Type


@model IEnumerable<KhazarCo.Models.Type>
@{
    ViewBag.Title = "Index";
    Layout = "~/Areas/Administrator/Views/Shared/_Layout.cshtml";
}
@section css
{<link href="@Url.Content("~/Content/themes/base/jquery-ui.css")" rel="stylesheet" type="text/css" />
}
@section js
{
    <script src="@Url.Content("~/Scripts/jquery-ui-1.9.0.min.js")" type="text/javascript"></script>
}


در آخر فایل Index.chtml  به اینصورت شده است:

<h2>
    نوع ها</h2>
<p>
    @Html.ActionLink("ایجاد یک مورد جدید", "Create", null, new { @class = "btn btn-info" })
</p>
<table>
    <thead>
        <tr>
            <th>
                عنوان
            </th>
            <th>
                توضیحات
            </th>
            <th>
                فعال
            </th>
            <th>
            </th>
        </tr>
    </thead>
    <tbody>
        @foreach (var item in Model.OrderBy(m => m.Priority))
        {
            <tr id="@item.Id">
                <td>
                    @Html.DisplayFor(modelItem => item.Title)
                </td>
                <td>
                    @(new HtmlString(item.Description))
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.IsActive)
                </td>
                <td>
                    @Html.ActionLink("ویرایش", "Edit", new { id = item.Id }, new { @class = "btnEdit label label-warning" })
                    |
                    @Html.ActionLink("مشاهده", "Details", new { id = item.Id }, new { @class = "btnDetails label label-info" })
                    |
                    @Html.ActionLink("حذف", "Delete", new { id = item.Id }, new { @class = "btnDelete label label-important" })
                </td>
            </tr>
        }
    </tbody>
</table>

** توجه داشته باشید که من به هر tr  یک id  اختصاص داده ام که این مقدار id  همان مقدار فیلد Id  همان رکورد هست ، ما برای مرتب کردن به این Id  نیاز داریم (خط 25).

افزودن کد‌های کلاینت:

حالا باید کدی بنویسم که دو کار را برای ما انجام دهد : اول حالت Sort  پذیری را به سطر‌های Table  بدهد و دوم اینکه هنگامی که ترتیب سطر‌های تغییر کرد ما را با خبر کند:


<script type="text/javascript">
    $(function () {
        $("table tbody").sortable({
            helper: fixHelper,
            update: function (event, ui) {
                jQuery.ajax('@Url.Action("Sort", "Type", new { area = "Administrator" })', {
                    data: { s: $(this).sortable('toArray').toString() }
                });
            }
        }).disableSelection();
    });
    var fixHelper = function (e, ui) {
        ui.children().each(function () {
            $(this).width($(this).width());
        });
        return ui;
    };
</script>


توضیح کد :

در این کد ما حالت ترتیب پذیری را به Table  می دهیم و هنگامی که عمل Update  در Table  انجام شد تابع مربوطه اجرا می‌شود. ما در این تایع، ترتیب جدید سطر‌ها را می‌گیریم ( ** به کمک مقدار Id  که به هر سطر دادیم ، این مقدار Id  برابر بود با Id خود رکورد در Database )  و به کمکjQuery.ajax  به تابع Sort  از کنترلر Type  در منطقه (area ) Administrator  ارسال می‌کنیم و در آنجا ادامه کار را انجام میدهیم.

تابع fixHelper  هم به ما کمک می‌کند که هنگامی که سطر‌ها از جای خود جدا می‌شوند ، دارای عرض یکسانی باشند و عرض آن‌ها تغییری نکند.


افزودن کد Server:

حالا باید تابع Sort  که مقادیر را به آن ارسال کردیم بنویسم. من این تابع را بر اساس مقداری که از کلاینت ارسال می‌شود اینگونه طراحی کردم.


        public EmptyResult Sort(string s)
        {
            if (s != null)
            {
                var ids = new List<int>();
                foreach (var item in s.Split(','))
                {
                    ids.Add(int.Parse(item));
                }
                int intpriority = 0;

                foreach (var item in ids)
                {
                    intpriority++;
                    db.Types.Single(m => m.Id == item).Priority = intpriority;
                }
                db.SaveChanges();
            }
            return new EmptyResult();
        }

در ایتدا مقادیر Id  که از کلاینت  به صورت String  ارسال شده است را می‌گیریم و بعد به همان ترتیب ارسال در لیستی از int قرار می‌دهیم ids.

سپس به اضای هر رکورد Type  مقدار اولویت را به فیلدی که برای همین مورد اضافه کردیم Priority اختصاص می‌دهیم. و در آخر هم تغییرات را ذخیره می‌کنیم. (خود کد کاملا واضح است و نیاری به توضیح بیشتر نیست )

حالا باید هنگامی که لیست Type  ها نمایش داده می‌شود به ترتیب (OrderBy) فیلد Priority    نمایش داده شود پس تابع Index را اینطور تغییر می‌دهیم.

        public ViewResult Index()
        {
            return View(db.Types.Where(m => m.IsDeleted == false).OrderBy(m => m.Priority));
        }

این هم خروجی کار من:

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


نظرات مطالب
ObservableCollection در Entity Framework
با سلام.
در پروژه WPF در لایه سرویس یکبار Local رابر میگردانم مانند زیر :
 public override IList<City> GetAll()
        {
            var query = from item in _tEntities
                        select item;
            query.Load();
            return _tEntities.Local;
        }
همه چیز درست است ولی وقتی برای جستجو متد زیر را اجرا می‌کنم باز Local شامل همان داده‌های قبلی است:
 public override IList<City> GetAll(Func<City, bool> predicate)
        {
            var query = from item in _tEntities
                        select item;
            query.Where<City>(predicate);
            query.Load();
            return _tEntities.Local;
        }
لطفاً راهنمایی کنید.

مطالب
مروری بر کتابخانه ReactJS - قسمت هفتم - ورودی‌های کاربر

تا به اینجا مثال‌هایی که زده‌ایم تاثیر کامپوننت‌های React را بر روی UI، نشان دادند. در این بخش به رویداد‌های سمت UI و ورودی‌های کاربر می‌پردازیم.


رویداد‌‌های ترکیبی  React

React روش مدیریت رویداد‌های خودش را دارد و به آنها رویداد‌های Synthetic یا ترکیبی گفته میشود. در زیر مقایسه‌ای داریم از رویداد‌های معمول در JavaScript و رویدادهای React و تفاوت‌ها را بررسی میکنیم.

<!-- HTML Buttons -->
<button type="button" onclick="console.log('Button Clicked')">Click Me</button>

// React Buttons
<button type="button" onClick={console.log("Button Clicked")}>Click Me</button>
  • باید نام رویداد‌ها را بصورت camelCase تایپ کنیم. 
  • از جاوااسکریپت به طور مستقیم استفاده میکنیم؛ نه بین quotation mark‌ها.
  • برای رویداد‌ها از توابع استفاده میکنیم و بهتر است تابع اجرایی هر رویداد در خود کامپوننت ساخته شود.
  • رویداد onClick در React به نوعی override شده رویداد onclick مرورگر است و به جای آن عمل میکند.

رفتار رویداد‌های React در مرورگر‌های مختلف یکسان است. برای مثال رویداد onChange هر تغییری را برای هر نوع تگ ورودی اعمال میکند. هر کلیدی که در یک input یا textarea زده شود، اگر یک check box را انتخاب یا از انتخاب خارج کنیم و یا اگر موردی را از یک drop-down انتخاب کنیم، React رویداد onChange را اجرا میکند. React اکثر رویداد‌های مرسوم را پوشش میدهد و همچنین رویداد‌هایی را برای کار با کلیپ‌برد، رسانه‌های مختلف و تصاویر دارد. برای اطلاعات بیشتر به مستندات آن رجوع کنید.

وقتی با کتابخانه React کار میکنیم، همه چیز مجازی اتفاق می‌افتد؛ مانند ساخت تگ و نمایش آنها، همچنین مدیریت تگ‌ها و رویدادها. اما به این معنا نیست که ارتباط React با HTML DOM در مرورگر قطع است. اگر لازم باشد به HTML DOM در کامپوننت‌ها دسترسی داشته باشیم میتوانیم از خاصیت ref در React استفاده کنیم. برای مثال فرض کنید یک ورودی را برای ایمیل به‌صورت <input type="email" /> تعریف کرده‌ایم. میخواهیم پیش از ذخیره بدانیم آیا داده وارد شده به فرمت ایمیل هست یا نه. 

const EmailForm = React.createClass({
    clickHandler() {
        if (this.inputEmail.checkValidity())
            console.log("Email is OK to save it.");
        else
            console.log("Email is not in right format.");
    },
    render() {
        return (
            <div>
                <input type="email" ref={inputEmail => this.inputEmail = inputEmail} />
                <button type="submit" onClick={this.clickHandler}>Save</button>
            </div>
        )
    }

در مثال بالا clickHandler وظیفه مدیریت رویداد کلیک دکمه را به عهده دارد. در ادامه، وقتی از خاصیت ref در تگ input استفاده میکنیم و مقدار آن را یک تابع قرار میدهیم، React این تابع را زمانیکه کامپوننت به طور کامل در HTML DOM ساخته شد، اجرا میکند. React همچنین ارجاعی را به عنوان پارامتر این تابع به DOM همراه با تابع ارسال میکند (inputEmail). داخل تابع ref میتوانیم به نمونه ساخته شده از کامپوننت در DOM دسترسی داشته باشیم. inputEmail که به صورت ارجاع به تابع فرستاده شده، تگ ساخته شده input را برمیگرداند، در نتیجه میتوانیم در کامپوننت به آن دسترسی داشته باشیم.

تغییر وضعیت کامپوننت

اگر از کامپوننت‌های Sateful که دارای وضعیت هستند استفاده میکنیم، میتوانیم وضعیت کامپوننت را بر اساس ورودی‌های کاربر تغییر دهیم. مثال بالا را به این شکل تغییر میدهیم که در ابتدا وضعیت کامپوننت، یک ایمیل پیش‌فرض باشد و اگر کاربر آدرس متفاوتی را وارد کرد، آدرس جدید به عنوان وضعیت جدید کامپوننت در نظر گرفته شود. 

const EmailForm = React.createClass({
    getInitialState() {
        return {
            currentEmail: this.props.currentEmail
        }
    },
    setCurrentEmailState(se) {
        this.setState({ currentEmail: se.target.value });
    },
    clickHandler() {
        if (this.inputEmail.checkValidity())
            console.log("Email is OK to save it.");
        else
            console.log("Email is not in right format.");
    },
    render() {
        return (
            <div>
                <input type="email" ref={inputEmail => this.inputEmail = inputEmail} 
                       value={this.state.currentEmail} onChange={this.setCurrentEmailState} />
                <button type="submit" onClick={this.clickHandler}>Save</button>
            </div>
        )
    }
})

در خط 20 از مثال بالا با قرار دادن مقدار value برابر با ایمیل جاری (وضعیت کامپوننت)، کاربر آدرس پیش‌فرض را در input میبیند، اما هیچ تغییری را نمیتواند در آن ایجاد کند و input عملا تبدیل به یک تگ فقط خواندنی میشود. علت این است که React دو وضعیت را ایجاد کرده، یکی در حافظه به عنوان وضعیت پیش‌فرض و دیگری وضعیتی که در DOM ساخته. وقتی در سطح DOM تغییری را ایجاد میکنیم، React به صورت خودکار متوجه آن نمیشود و ما باید با روشی React را در جریان این تغییرات قرار دهیم! برای این کار رویداد onChange را برای تگی که قرار است تغییر کند پیاده‌سازی میکنیم. در مثال بالا متد setCurrentEmailState و رویداد onChange برای همین منظور به کار گرفته شده‌اند. 

در قسمت بعد که آخرین قسمت است، به مسئله چرخه زندگی (Lifecycle) کامپوننت‌های React میپردازیم.

مطالب
آموزش Linq - بخش ششم : عملگرهای پرس و جو قسمت دوم
در ادامه‌ی سری آموزشی LINQ، عملگر‌های پرس و جوی مرتب سازی، گروه بندی و مجموعه را بررسی خواهیم کرد.

عملگرهای مرتب سازی  Ordering Operators
این عملگر‌ها عناصر توالی ورودی را به خروجی ارسال می‌کنند؛ با این تفاوت که توالی خروجی مرتب شده، توالی ورودی است.

عملگر OrderBy

این عملگر توالی ورودی را بر اساس کلیدی که مشخص می‌کنیم مرتب می‌کند.
مثال:
در این مثال کلید معرفی شده‌ی توسط عبارت Lambda، یک رشته است.
string[] ingredients = { "Sugar", "Egg", "Milk", "Flour", "Butter" };
var query = ingredients.OrderBy(x => x);
foreach (var item in query)
{
   Console.WriteLine(item);
}
خروجی مثال بالا:
 Butter
Egg
Flour
Milk
Sugar
همان طور که ملاحظه می‌کنید، عملگر OrderBy به‌صورت پیش فرض مرتب سازی عناصر را صعودی انجام داده است.
عبارت Lambda استفاده شده می‌تواند یک خاصیت از عناصر تشکیل دهنده‌ی توالی ورودی باشد.
مثال:
Ingredient[] ingredients =
{
   new Ingredient {Name = "Sugar", Calories = 500},
   new Ingredient {Name = "Egg", Calories = 100},
   new Ingredient {Name = "Milk", Calories = 150},
   new Ingredient {Name = "Flour", Calories = 50},
   new Ingredient {Name = "Butter", Calories = 200},
};
var query = ingredients.OrderBy(x => x.Calories);
foreach (var item in query)
{
   Console.WriteLine(item.Name + " " + item.Calories);
}
خروجی مثال بالا:
Flour 50
Egg 100
Milk 150
Butter 200
Sugar 500
همانطور که مشاهده می‌کنید توالی خروجی، بر اساس خصوصیت کالری توالی ورودی مرتب شده و در خروجی نمایش داده شده است.

پیاده سازی توسط عبارت‌های جستجو
در عبارت‌های پرس و جو، کلمه کلیدی orderby برای مرتب سازی توالی استفاده می‌شود:
مثال:
var query = from i in ingredients
orderby i.Calories
select i;

عملگر ThenBy

این عملگر می‌تواند به صورت زنجیره‌ای، یک یا چندین بار بعد از عملگر OrderBy در پرس و جو، مورد استفاده قرار گیرد. توسط عملگر ThenBy می‌توان سطوح دیگری از مرتب سازی را اعمال کرد. در مثال زیر ابتدا توالی ورودی را بر اساس کالری و بعد از آن بر اساس نام مرتب خواهیم کرد.
مثال :
Ingredient[] ingredients =
{
   new Ingredient {Name = "Sugar", Calories = 500},
   new Ingredient {Name = "Milk", Calories = 100},
   new Ingredient {Name = "Egg", Calories = 100},
   new Ingredient {Name = "Flour", Calories = 500},
   new Ingredient {Name = "Butter", Calories = 200},
};
var query = ingredients
                     .OrderBy(x => x.Calories)
                     .ThenBy(x => x.Name);
foreach (var item in query)
{
   Console.WriteLine(item.Name + " " + item.Calories);
}
خروجی مثال بالا:
Egg 100
Milk 100
Butter 200
Flour 500
Sugar 500
در اینجا در توالی ورودی، Milk قبل از Egg قرار دارد. ولی به خاطر استفاده از عملگر ThenBy، در مواردی که کالری عناصر یکسان است، بر اساس نام عناصر توالی، مرتب سازی انجام شده است.

پیاده سازی توسط عبارت‌های جستجو
var query = from i in ingredients
orderby i.Calories, i.Name
select i;
همانطور که مشاهده می‌کنید برای مرتب سازی بر اساس خصوصیات دیگر کافیست نام خصوصیت را بعد از اولین عنصر، به وسیله‌ی کاما (,) قید کنید.

عملگر OrderByDescending
این عملگر همچون عملگر OrderBy عمل می‌کند؛ اما توالی ورودی را بر اساس کلید داده شده، به صورت نزولی مرتب می‌کند.
مثال:
Ingredient[] ingredients =
{
   new Ingredient {Name = "Sugar", Calories = 500},
   new Ingredient {Name = "Egg", Calories = 100},
   new Ingredient {Name = "Milk", Calories = 150},
   new Ingredient {Name = "Flour", Calories = 50},
   new Ingredient {Name = "Butter", Calories = 200},
};
var query = ingredients.OrderByDescending(x => x.Calories);
foreach (var item in query)
{
   Console.WriteLine(item.Name + " " + item.Calories);
}
خروجی مثال بالا:
Sugar 500
Flour 500
Butter 200
Milk 100
Egg 100

پیاده سازی توسط عبارت‌های جستجو

جایگزین عملگر OrderByDescending در عبارت‌های جستجو، کلمه‌ی کلیدی descending می‌باشد:
var query = from i in ingredients
orderby i.Calories descending
select i;

عملگر ThenByDescending

این عملگر بصورت زنجیره‌ای بعد از عملگر‌های دیگر می‌آید و مرتب سازی را به‌صورت نزولی انجام می‌دهد.
مثال:
Ingredient[] ingredients =
{
   new Ingredient {Name = "Flour", Calories = 500},
   new Ingredient {Name = "Sugar", Calories = 500},
   new Ingredient {Name = "Egg", Calories = 100},
   new Ingredient {Name = "Milk", Calories = 100},
   new Ingredient {Name = "Butter", Calories = 200}
};
var query = ingredients
                    .OrderBy(x => x.Calories)
                    .ThenByDescending(x => x.Name);
foreach (var item in query)
{
   Console.WriteLine(item.Name + " " + item.Calories);
}
خروجی مثال بالا:
Milk 100
Egg 100
Butter 200
Sugar 500
Flour 500
در این مثال در توالی ورودی، Flour قبل از Sugar آمده است. اما به خاطر عملگر ThenOrderByDescending ترتیب قرار گیری آنها در توالی خروجی تغییر کرده است.

پیاده سازی توسط عبارت‌های جستجو
در عبارت‌های جستجو نیز برای رسیدن به خروجی مشابه کد بالا از کلمه‌ی کلیدی descending استفاده می‌کنیم.
var query = from i in ingredients
orderby i.Calories, i.Name descending
select i;

عملگر Reverse

عملگر Reveres توالی ورودی را دریافت کرده و آن را معکوس و سپس توالی خروجی را تولید می‌کند. اولین عنصر توالی ورودی، آخرین عنصر توالی خروجی می‌باشد.
مثال:
char[] letters = { 'A', 'B', 'C' };
var query = letters.Reverse();
foreach (var item in query)
{
   Console.WriteLine(item);
}
خروجی مثال بالا:
C
B
A

پیاده سازی توسط عبارت‌های جستجو

معادل این عملگر، کلمه‌ی کلیدی دیگری در عبارت‌های جستجو وجود ندارد و ترکیب دو روش می‌تواند خروجی دلخواه را تولید کند.


عملگر‌های گروه بندی Grouping Operator

عملگر GroupBy

این عملگر یک توالی ورودی را دریافت کرده و یک توالی گروه بندی شده را به خروجی ارسال می‌کند. پایه‌ای‌ترین امضاء متد GroupBy، یک عبارت Lambda می‌باشد .این عبارت، کلید گروه بندی توالی ورودی را مشخص می‌کند.
مثال: در این مثال قصد داریم مواد غذایی مختلف را بر اساس کالری آنها گروه بندی کنیم.
Ingredient[] ingredients =
{
   new Ingredient {Name = "Sugar", Calories = 500},
   new Ingredient {Name = "Lard", Calories = 500},
   new Ingredient {Name = "Butter", Calories = 500},
   new Ingredient {Name = "Egg", Calories = 100},
   new Ingredient {Name = "Milk", Calories = 100},
   new Ingredient {Name = "Flour", Calories = 50},
   new Ingredient {Name = "Oats", Calories = 50}
};
IEnumerable<IGrouping<int, Ingredient>> query = ingredients.GroupBy(x => x.Calories);
foreach (IGrouping<int, Ingredient> group in query)
{
   Console.WriteLine("Ingredients with {0} calories", group.Key);
   foreach (Ingredient ingredient in group)
   {
     Console.WriteLine(" -{0}", ingredient.Name);
   }
}
در مثال فوق خروجی تابع GroupBy یک لیست قابل شمارش از نوع IGrouping می‌باشد. هر عنصر IGrouping شامل یک کلید (در اینجا کالری مواد غذایی) و لیستی از مواد غذایی که کالری‌های یکسانی دارند، می‌باشد.
خروجی مثال بالا:
 Ingredients with 500 calories
 -Sugar
 -Lard
 -Butter
Ingredients with 100 calories
 -Egg
 -Milk
Ingredients with 50 calories
 -Flour
 -Oats

پیاده سازی توسط عبارت‌های جستجو
در بخش پنجم این سری آموزشی، روش گروه بندی توسط عبارت‌های جستجو توضیح داده شده است.


عملگرهای مجموعه Set Operators
این عملگر‌ها شامل موارد زیر می‌باشند:
• Concat
• Union
• Distinct
• Intersect
• Except

عملگر Concat

این عملگر دو توالی را با هم ادغام می‌کند؛ بطوریکه عناصر توالی دوم، بعد از عناصر توالی اول به توالی خروجی اضافه می‌شوند.
مثال:
string[] applePie = { "Apple", "Sugar", "Pastry", "Cinnamon" };
string[] cherryPie = { "Cherry", "Sugar", "Pastry", "Kirsch" };
IEnumerable<string> query = applePie.Concat(cherryPie);
foreach (string item in query)
{
   Console.WriteLine(item);
}
خروجی مثال بالا :
Apple
Sugar
Pastry
Cinnamon
Cherry
Sugar
Pastry
Kirsch
توجه داشته باشید که در این حالت عناصر تکراری حذف نخواهند شد.

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

عملگر Union
این عملگر همچون عملگر Concat رفتار می‌کند؛ با این تفاوت که عناصر تکراری در توالی خروجی حضور نخواهند داشت.
مثال:
string[] applePie = { "Apple", "Sugar", "Pastry", "Cinnamon" };
string[] cherryPie = { "Cherry", "Sugar", "Pastry", "Kirsch" };
IEnumerable<string> query = applePie.Union(cherryPie);
foreach (string item in query)
{
   Console.WriteLine(item);
}
خروجی مثال بالا:
Apple
Sugar
Pastry
Cinnamon
Cherry
Kirsch
همانطور که مشاهده می‌کنید، عناصر Sugar و Pastry که در توالی دوم تکرار شده بودند، در خروجی وجود ندارند.

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

عملگر  Distinct
این عملگر عناصر تکراری توالی را حذف می‌کند. این عملگر هم می‌تواند بر روی یک توالی اجرا شود و هم می‌تواند بصورت زنجیره‌ای بعد از عملگر‌های دیگر بکار برود.
مثال: استفاده از این عملگر بر روی یک توالی:
string[] applePie = { "Apple", "Sugar", "Apple", "Sugar", "Pastry", "Cinnamon" };
IEnumerable<string> query = applePie.Distinct();
foreach (string item in query)
{
   Console.WriteLine(item);
}
در مثال بالا، عناصر تکراری در توالی ورودی را، از طریق عملگر Distinct حذف کرده‌ایم.
خروجی مثال بالا:
Apple
Sugar
Pastry
Cinnamon

مثال: بکارگیری عملگر Distinct بهمراه عملگر Concat برای شبیه سازی عملیات Union
string[] applePie = { "Apple", "Sugar", "Pastry", "Cinnamon" };
string[] cherryPie = { "Cherry", "Sugar", "Pastry", "Kirsch" };
IEnumerable<string> query = applePie.Concat(cherryPie).Distinct();

foreach (string item in query)
{
   Console.WriteLine(item);
}
خروجی مثال بالا:
Apple
Sugar
Pastry
Cinnamon
Cherry
Kirsch
همانطور که مشاهده می‌کنید خروجی این مثال با حالت استفاده از Union تفاوتی ندارد.

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

عملگر Intersect
این عملگر عناصر مشترک بین دو توالی را باز می‌گرداند.
مثال:
string[] applePie = { "Apple", "Sugar", "Pastry", "Cinnamon" };
string[] cherryPie = { "Cherry", "Sugar", "Pastry", "Kirsch" };
IEnumerable<string> query = applePie.Intersect(cherryPie);
foreach (string item in query)
{
   Console.WriteLine(item);
}
خروجی مثال بالا:
Sugar
Pastry
نکته: عناصر تکراری فقط یکبار در خروجی ظاهر خواهند شد.

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

عملگر Except
این عملگر عناصری از توالی اول را انتخاب می‌کند که در توالی دوم همتایی نداشته باشند.
مثال:
string[] applePie = { "Apple", "Sugar", "Pastry", "Cinnamon" };
string[] cherryPie = { "Cherry", "Sugar", "Pastry", "Kirsch" };
IEnumerable<string> query = applePie.Except(cherryPie);
foreach (string item in query)
{
   Console.WriteLine(item);
}
خروجی مثال فوق:
Apple
Cinnamon
نکته: هیچ عنصری از توالی دوم در خروجی وجود نخواهد داشت.

پیاده سازی توسط عبارت‌های جستجو
معادل این عملگر، کلمه‌ی کلیدی جدیدی در عبارت‌های جستجو وجود ندارد و ترکیب دو روش می‌تواند خروجی دلخواه را تولید کند.
اشتراک‌ها
مثال هایی از نحوه نوشتن PBI
News  
  • As a site visitor, I can read current news on the home page.
  • As a site visitor, I can access old news that is no longer on the home page.
  • As a site visitor, I can email news items to the editor. (Note: this could just be an email link to
  • the editor.)
  • As a site a site editor, I can set the following dates on a news item: Start Publishing Date, Old
  • News Date, Stop Publishing Date. These dates refer to the date an item becomes visible on the site (perhaps next Monday), the date it stops appearing on the home page, and the date it is removed from the site (which may be never).
  • As a site member, I can subscribe to an RSS feed of news (and events? Or are they separate?).
  • As a site editor, I can assign priority numbers to news items. Items are displayed on the front page based on priority.  
مثال هایی از نحوه نوشتن PBI
مطالب
ساختار داده‌های خطی Linear Data Structure قسمت دوم
در قسمت قبلی به مقدمات و ساخت لیست‌های ایستا و پویا به صورت دستی پرداختیم و در این قسمت (مبحث پایانی) لیست‌های آماده در دات نت را مورد بررسی قرار می‌دهیم.

کلاس ArrayList
این کلاس همان پیاده سازی لیست‌های ایستایی را دارد که در مطلب پیشین در مورد آن صحبت کردیم و نحوه کدنویسی آن نیز بیان شد و امکاناتی بیشتر از آنچه که در جدول مطلب پیشین گفته بودیم در دسترس ما قرار می‌دهد. از این کلاس با اسم untyped dynamically-extendable array به معنی آرایه پویا قابل توسعه بدون نوع هم اسم می‌برند چرا که به هیچ نوع داده‌ای مقید نیست و می‌توانید یکبار به آن رشته بدهید، یکبار عدد صحیح، یکبار اعشاری و یکبار زمان و تاریخ، کد زیر به خوبی نشان دهنده‌ی این موضوع است و نحوه استفاده‌ی از این آرایه‌ها را نشان می‌دهد.
using System;
using System.Collections;
 
class ProgrArrayListExample
{
    static void Main()
    {
        ArrayList list = new ArrayList();
        list.Add("Hello");
        list.Add(5);
        list.Add(3.14159);
        list.Add(DateTime.Now);
 
        for (int i = 0; i < list.Count; i++)
        {
            object value = list[i];
            Console.WriteLine("Index={0}; Value={1}", i, value);
        }
    }
}
نتیجه کد بالا:
Index=0; Value=Hello
Index=1; Value=5
Index=2; Value=3.14159
Index=3; Value=29.02.2015 23:17:01
البته برای خواندن و قرار دادن متغیرها از آنجا که فقط نوع Object را برمی‌گرداند، باید یک تبدیل هم انجام داد یا اینکه از کلمه‌ی کلیدی dynamic استفاده کنید:
ArrayList list = new ArrayList();
list.Add(2);
list.Add(3.5f);
list.Add(25u);
list.Add(" ریال");
dynamic sum = 0;
for (int i = 0; i < list.Count; i++)
{
    dynamic value = list[i];
    sum = sum + value;
}
Console.WriteLine("Sum = " + sum);
// Output: Sum = 30.5ریال

مجموعه‌های جنریک Generic Collections
مشکل ما در حین کار با کلاس arrayList و همه کلاس‌های مشتق شده از system.collection.IList این است که نوع داده‌ی ما تبدیل به Object می‌شود و موقعی‌که آن را به ما بر می‌گرداند باید آن را به صورت دستی تبدیل کرده یا از کلمه‌ی کلیدی dynamic استفاده کنیم. در نتیجه در یک شرایط خاص، هیچ تضمینی برای ما وجود نخواهد داشت که بتوانیم کنترلی بر روی نوع داده‌های خود داشته باشیم و به علاوه عمل تبدیل یا casting هم یک عمل زمان بر هست.
برای حل این مشکل، از جنریک‌ها استفاده می‌کنیم. جنریک‌ها می‌توانند با هر نوع داده‌ای کار کنند. در حین تعریف یک کلاس جنریک نوع آن را مشخص می‌کنیم و مقادیری که از آن به بعد خواهد پذیرفت، از نوعی هستند که ابتدا تعریف کرده‌ایم.
یک ساختار جنریک به صورت زیر تعریف می‌شود:
GenericType<T> instance = new GenericType<T>();
نام کلاس و به جای T نوع داده از قبیل int,bool,string را می‌نویسیم. مثال‌های زیر را ببینید:
List<int> intList = new List<int>();
List<bool> boolList = new List<bool>();
List<double> realNumbersList = new List<double>();

کلاس جنریک <List<T
این کلاس مشابه همان کلاس ArrayList است و فقط به صورت جنریک پیاده سازی شده است.
List<int> intList = new List<int>();
تعریف بالا سبب ایجاد ArrayList ـی می‌باشد که تنها مقادیر int را دریافت می‌کند و دیگر نوع Object ـی در کار نیست. یک آرایه از نوع int ایجاد می‌کند و مقدار خانه‌های پیش فرضی را نیز در ابتدا، برای آن در نظر می‌گیرد و با افزودن هر مقدار جدید می‌بیند که آیا خانه‌ی خالی وجود دارد یا خیر. اگر وجود داشته باشد مقدار جدید، به خانه‌ی بعدی آخرین خانه‌ی پر شده انتقال می‌یابد و اگر هم نباشد، مقدار خانه از آن چه هست 2 برابر می‌شود. درست است عملیات resizing یا افزایش طول آرایه عملی زمان بر محسوب میشود ولی همیشه این اتفاق نمی‌افتد و با زیاد شدن مقادیر خانه‌ها این عمل کمتر هم می‌شود. هر چند با زیاد شدن خانه‌ها حافظه مصرفی ممکن است به خاطر زیاد شدن خانه‌های خالی بدتر هم بشود. فرض کنید بار اول خانه‌ها 16 تایی باشند که بعد می‌شوند 32 تایی و بعدا 64 تایی. حالا فرض کنید به خاطر یک عنصر، خانه‌ها یا ظرفیت بشود 128 تایی در حالی که طول آرایه (خانه‌های پر شده) 65 تاست و حال این وضعیت را برای موارد بزرگتر پیش بینی کنید. در این نوع داده اگر منظور زمان باشد نتجه خوبی را در بر دارد ولی اگر مراعات حافظه را هم در نظر بگیرید و داده‌ها زیاد باشند، باید تا حدامکان به روش‌های دیگر هم فکر کنید.

چه موقع از <List<T استفاده کنیم؟
استفاده از این روش مزایا و معایبی دارد که باید در توضیحات بالا متوجه شده باشید ولی به طور خلاصه:
  • استفاده از index برای دسترسی به یک مقدار، صرف نظر از اینکه چه میزان داده‌ای در آن وجود دارد، بسیار سریع انجام میگیرد.
  • جست و جوی یک عنصر بر اساس مقدار: جست و جو خطی است در نتیجه اگر مقدار مورد نظر در آخرین خانه‌ها باشد بدترین وضعیت ممکن رخ می‌دهد و بسیار کند عمل می‌کند. داده هر چی کمتر بهتر و هر چه بیشتر بدتر. البته اگر بخواهید مجموعه‌ای از مقدارهای برابر را برگردانید هم در بدترین وضعیت ممکن خواهد بود.
  • حذف و درج (منظور insert) المان‌ها به خصوص موقعی که انتهای آرایه نباشید، شیفت پیدا کردن در آرایه عملی کاملا کند و زمانبر است.
  • موقعی که عنصری را بخواهید اضافه کنید اگر ظرفیت آرایه تکمیل شده باشد، نیاز به عمل زمانبر افزایش ظرفیت خواهد بود که البته این عمل به ندرت رخ می‌دهد و عملیات افزودن Add هم هیچ وابستگی به تعداد المان‌ها ندارد و عملی سریع است.
با توجه به موارد خلاصه شده بالا، موقعی از لیست اضافه می‌کنیم که عملیات درج و حذف زیادی نداریم و بیشتر برای افزودن مقدار به انتها و دسترسی به المان‌ها بر اساس اندیس باشد.

<LinkedList<T
یک کلاس از پیش آماده در دات نت که لیست‌های پیوندی دو طرفه را پیاده سازی می‌کند. هر المان یا گره یک متغیر جهت ذخیره مقدار دارد و یک اشاره گر به گره قبل و بعد.
چه موقع باید از این ساختار استفاده کنیم؟
از مزایا و معایب آن :
  • افزودن به انتهای لیست به خاطر این که همیشه گره آخر در tail وجود دارد بسیار سریع است.
  • عملیات درج insert در هر موقعیتی که باشد اگر یک اشاره گر به آن محل باشد یک عملیات سریع است یا اینکه درج در ابتدا یاانتهای لیست باشد.
  • جست و جوی یک مقدار چه بر اساس اندیس باشد و چه مقدار، کار جست و جو کند خواهد بود. چرا که باید تمامی المان‌ها از اول به آخر اسکن بشن.
  • عملیات حذف هم به خاطر اینکه یک عمل جست و جو در ابتدای خود دارد، یک عمل کند است.
استفاده از این کلاس موقعی خوب است که عملیات‌های درج و حذف ما در یکی از دو طرف لیست باشد یا اشاره‌گری به گره مورد نظر وجود داشته باشد. از لحاظ مصرف حافظه به خاطر داشتن فیلدهای اشاره‌گر به جز مقدار، زیاد‌تر از نوع List می‌باشد. در صورتی که دسترسی سریع به داده‌ها برایتان مهم باشد استفاده از List باز هم به صرفه‌تر است.

پشته Stack
یک سری مکعب را تصور کنید که روی هم قرار گرفته اند و برای اینکه به یکی از مکعب‌های پایینی بخواهید دسترسی داشته باشید باید تعدادی از مکعب‌ها را از بالا بردارید تا به آن برسید. یعنی بر خلاف موقعی که آن‌ها روی هم می‌گذاشتید و آخرین مکعب روی همه قرار گرفته است. حالا همان مکعب‌ها به صورت مخالف و معکوس باید برداشته شوند.
یک مثال واقعی‌تر و ملموس‌تر، یک کمد لباس را تصور کنید که مجبورید برای آن که به لباس خاصی برسید، باید آخرین لباس‌هایی را که در داخل کمد قرار داده‌اید را اول از همه از کمد در بیاورید تا به آن لباس برسید.
در واقع  پشته چنین ساختاری را پیاده می‌کند که اولین عنصری که از پشته بیرون می‌آید، آخرین عنصری است که از آن درج شده است و به آن LIFO گویند که مخفف عبارت Last Input First Output آخرین ورودی اولین خروجی است. این ساختار از قدیمی‌ترین ساختارهای موجود است. حتی این ساختار در سیستم‌های داخل دات نت CLR هم به عنوان نگهدارنده متغیرها و پارامتر متدها استفاده می‌شود که به آن Program Execution Stack می‌گویند.
پشته سه عملیات اصلی را پیاده سازی می‌کند: Push جهت قرار دادن مقدار جدید در پشته، POP جهت بیرون کشیدن مقداری که آخرین بار در پشته اضافه شده و Peek جهت برگرداندن آخرین مقدار اضافه شده به پشته ولی آن مقدار از پشته حذف نمی‌شود.
این ساختار میتواند پیاده سازی‌های متفاوتی را داشته باشد ولی دو نوع اصلی که ما بررسی می‌کنیم، ایستا و پویا بودن آن است. ایستا بر اساس آرایه است و پویا بر اساس لیست‌های پیوندی. شکل زیر پشته‌ای را به صورت استفاده از پیاده‌سازی ایستا با آرایه‌ها نشان می‌دهد و کلمه Top به بالای پشته یعنی آخرین عنصر اضافه شده اشاره می‌کند.

استفاده از لیست پیوندی برای پیاده سازی پشته:

لیست پیوندی لازم نیست دو طرفه باشد و یک طرف برای کار با پشته مناسب است و دیگر لازم نیست که به انتهای لیست پیوندی عمل درج انجام شود؛ بلکه مقدار جدید به ابتدای آن اضافه شده و برای حذف گره هم اولین گره باید حذف شود و گره دوم به عنوان head شناخته می‌شود. همچنین لیست پیوندی نیازی به افزایش ظرفیت مانند آرایه‌ها ندارد.
ساختار پشته در دات نت توسط کلاس Stack از قبل آماده است:
Stack<string> stack = new Stack<string>();
stack.Push("A");
stack.Push("B");
stack.Push("C");
 while (stack.Count > 0)
    {
        string letter= stack.Pop();
        Console.WriteLine(letter);
    }
//خروجی
//C
//B
//A

صف Queue
ساختار صف هم از قدیمی‌ترین ساختارهاست و مثال آن در همه جا و در همه اطراف ما دیده می‌شود؛ مثل صف نانوایی، صف چاپ پرینتر، دسترسی به منابع مشترک توسط سیستمها. در این ساختار ما عنصر جدید را به انتهای صف اضافه می‌کنیم و برای دریافت مقدار، عنصر را از ابتدا حذف می‌کنیم. به این ساختار FIFO مخفف First Input First Output به معنی اولین ورودی و اولین خروجی هم می‌گویند.
ساختار ایستا که توسط آرایه‌ها پیاده سازی شده است:

ابتدای آرایه مکانی است که عنصر از آنجا برداشته می‌شود و Head به آن اشاره می‌کند و tail هم به انتهای آرایه که جهت درج عنصر جدید مفید است. با برداشتن هر خانه‌ای که head به آن اشاره می‌کند، head یک خانه به سمت جلو حرکت می‌کند و زمانی که Head از tail بیشتر شود، یعنی اینکه دیگر عنصری یا المانی در صف وجود ندارد و head و Tail به ابتدای صف حرکت می‌کنند. در این حالت موقعی که المان جدیدی قصد اضافه شدن داشته باشد، افزودن، مجددا از اول صف آغاز می‌شود و به این صف‌ها، صف حلقوی می‌گویند.

عملیات اصلی صف دو مورد هستند enqueue که المان جدید را در انتهای صف قرار می‌دهد و dequeue اولین المان صف را بیرون می‌کشد.


پیاده سازی صف به صورت پویا با لیست‌های پیوندی

برای پیاده سازی صف، لیست‌های پیوندی یک طرفه کافی هستند:

در این حالت عنصر جدید مثل سابق به انتهای لیست اضافه می‌شود و برای حذف هم که از اول لیست کمک می‌گیریم و با حذف عنصر اول، متغیر Head به عنصر یا المان دوم اشاره خواهد کرد.

کلاس از پیش آمده صف در دات نت <Queue<T است و نحوه‌ی استفاده آن بدین شکل است:

static void Main()
{
    Queue<string> queue = new Queue<string>();
    queue.Enqueue("Message One");
    queue.Enqueue("Message Two");
    queue.Enqueue("Message Three");
    queue.Enqueue("Message Four");
 
    while (queue.Count > 0)
    {
        string msg = queue.Dequeue();
        Console.WriteLine(msg);
    }
}
//خروجی
//Message One
//Message Two
//Message Thre
//Message Four