با سلام ؛
بخش اول :
استفاده از LINQ to Objects در #C
بخش دوم:
ایجاد یک DataTable از یک Query
بخش سوم :امکانات و قابلیت های LINQ برای مدیران پروژه ها
بخش چهارم :LINQ To SQL
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
استفاده از LINQ to Objects در #C
مقدمه
این مقاله، مقدمه ایست بر استفاده از queryهای LINQ to Objects جهت ساپورت یک برنامه windows forms. این مقاله، ساختار عبارات LINQ to Objects را مورد بحث قرار می دهد و توضیح می دهد چگونه می توان از LINQ to Objects بدون بافت (context) یک برنامه واقعی استفاده کرد.
پروژه ای که در این مقاله آورده شده است، یک contact manager ساده است که ممکن است برای capture کردن و ذخیره اطلاعات مخاطب های یک شخص در قالب دفتر تلفن، مورد استفاده قرار گیرد. این برنامه از LINQ to Objects برای مدیریت، query کردن و مرتب کردن لیست مخاطبین، استفاده می کند. همچنین این برنامه شامل یک فایل contact مبتدی با مجموعه ای از مخاطبین تستی است.
شکل 1: فرم اصلی برنامه
[ برای مشاهده لینک ، با نام کاربری خود وارد شوید یا ثبت نام کنید ]
این برنامه دارای عملکردهای ریز است:
- یک فایل اطلاعات مخاطبین ایجاد می کند.
- مخاطبین را به فایل اطلاعات مخاطبین اضافه می کند.
- مخاطبین را از فایل اطلاعات مخاطبین حذف می کند.
- مخاطبین را بر اساس نام خانوادگی شان جستجو می کند.
- جزییات مخاطبین را ایجاد و ویرایش می کند.
- نام
- نام خانوادگی
- خیابان
- شهر
- ایالت
- کد پستی
- تلفن منزل
- تلفن محل کار
- تلفن همراه
- آدرس ای میل
معمولاً، روش های مورد استفاده در این برنامه، نمایانگر یکی از چندین روشی انجام آن کارها است، مانند خیلی از چیزها در دنیای .Net، چندین جایگزین وجود دارد و اگر مایل باشید می توانید با استفاده از جایگزین های دیگر، کد را برای کارکردن با داده ها تغییر دهید.
- فایل اطلاعات مخاطبین را ذخیره می کند.
- فایل اطلاعات مخاطبین را دوباره باز می کند.
- همه مخاطبین را در فایل اطلاعات مخاطبین پیمایش می کند.
- می توان لیستی از همه مخاطبین را در فایل اطلاعات مخاطبین مشاهده کرد.
- امکان جستجو را بر اساس حرف آغازین نام خانوادگی فراهم می کند (Rolodex).
شکل 2: جستجوی یک مخاطب بر اساس نام خانوادگی
[ برای مشاهده لینک ، با نام کاربری خود وارد شوید یا ثبت نام کنید ]
شکل 3: لیست کردن همه مخاطبین:
[ برای مشاهده لینک ، با نام کاربری خود وارد شوید یا ثبت نام کنید ]
شکل 4: عملکرد Rolodex
[ برای مشاهده لینک ، با نام کاربری خود وارد شوید یا ثبت نام کنید ]
عبارات LINQ to Objects
این بخش، بعضی از تکنیک های رایج و مورد استفاده در ساختار عبارات LINQ to Objects را مورد بحث قرار می دهد. به طور خلاصه، LINQ to Objects، ابزاری برای مدیریت queryهایی در collectionهای درون حافظه در اختیار برنامه نویسان قرار می دهد. این تکنیک ها که برای query کردن چنین collectionهایی استفاده می شوند، شبیه روش هایی هستند که برای مدیریت queryها در databaseهای رابطه ای استفاده می شوند، ولی ساده تر هستند.
آناتومی عبارات LINQ to Objects
مثال 1: یک Select ساده
این، مثالی بسیار ساده از عبارات LINQ to Objects است:
کد:string[] tools = { "Tablesaw", "Bandsaw", "Planer", "Jointer", "Drill",
"Sander" };
var list = from t in tools
select t;
StringBuilder sb = new StringBuilder();
foreach (string s in list)
{
sb.Append(s + Environment.NewLine);
}
MessageBox.Show(sb.ToString(), "Tools");
در این مثال، آرایه ای از رشته ها (ابزارها) به صورت collectionهایی از اشیا استفاده شده تا با استفاده از LINQ to Objects، کوئری شوند.
کوئری LINQ to Objects:
در این مثال، یک متغیر بی نوع (untyped variable) به نام "list" ایجاد می شود و همه آیتم های موجود در string array به این این object اضافه می شوند، این typeها هر کدام دارای معنای خاصی است، مثلاً "t" بمعنای tools است؛ از آنجاییکه "tools" یک string array است، framework استنباط می کند که "t" یک string نیز هست. البته، این موضوع آنقدرها هم افتضاح نیست، زیرا شما می توانید array را تکرار کنید تا لزوماً همان کار را انجام دهید؛ اما می توانید queryهای پیچیده تری را با LINQ to Objects ایجاد کنید.کد:var list = from t in tools
select t;
اگر قرار بود یک پروژه ایجاد کنید، این کد را به یک متد اضافه وآن را اجرا کنید، نتایج به این صورت خواهد بود:
شکل 5: query نتایج
[ برای مشاهده لینک ، با نام کاربری خود وارد شوید یا ثبت نام کنید ]
مثال 2: Select با یک Where Clause
این مثال، یک کوئری LINQ to Objects را نشان می دهد که شامل یک where clause می شود. در این مثال، با مجموعه ای پرنده ها به شکل یک string array شروع می کنیم؛ از LINQ to Objects برای query کردن این string array استفاده می شود تا یک subset از array را به شکل همه پرندگانی که با حرف R شروع می شوند پیدا کند و بازگرداند.
اگر قرار بود این query را اجرا کنیم، نتایج به صورت زیر ظاهر می شد (همه پرندگانی که با R شروع می شوند، نشان داده شده اند):کد:string[] Birds = { "Indigo Bunting", "Rose Breasted Grosbeak", "Robin",
"House Finch", "Gold Finch", "Ruby Throated
Hummingbird","Rufous Hummingbird", "Downy Woodpecker"
};
var list = from b in Birds
where b.StartsWith("R")
select b;
StringBuilder sb = new StringBuilder();
foreach (string s in list)
{
sb.Append(s + Environment.NewLine);
}
MessageBox.Show(sb.ToString(), "R Birds");
شکل 6: نتاج کوئری پرندگان R:
[ برای مشاهده لینک ، با نام کاربری خود وارد شوید یا ثبت نام کنید ]
مثال 3: Select با یک Where Clause
این کوئری، با اندک تفاوتی نسبت به مثال قبلی، بدنبال یک جفت دقیق در کلمه where است:
اجرای این کد منجر به نمایش پنجره زیر خواهد شد:کد:string[] Birds = { "Indigo Bunting", "Rose Breasted Grosbeak", "Robin",
"House Finch", "Gold Finch", "Ruby Throated
Hummingbird","Rufous Hummingbird", "Downy Woodpecker"
};
var list = from b in Birds
where b == "House Finch"
select b;
StringBuilder sb = new StringBuilder();
foreach (string s in list)
{
sb.Append(s + Environment.NewLine);
}
MessageBox.Show(sb.ToString(), "Found Bird");
شکل 7: نتایج کوئری پرنده
[ برای مشاهده لینک ، با نام کاربری خود وارد شوید یا ثبت نام کنید ]
مثال 4: generate کردن یک Ordered List
در این کوئری، لیستی از پرندگان به ترتیب حروف الفبا مرتب شده است:
شکل 8: نتایج کوئری Ordered Bird Listکد:string[] Birds = { "Indigo Bunting", "Rose Breasted Grosbeak", "Robin",
"House Finch", "Gold Finch", "Ruby Throated
Hummingbird","Rufous Hummingbird", "Downy Woodpecker"
};
var list = from b in Birds
orderby b ascending
select b;
StringBuilder sb = new StringBuilder();
foreach (string s in list)
{
sb.Append(s + Environment.NewLine);
}
MessageBox.Show(sb.ToString(), "Alphabetized Birds");
[ برای مشاهده لینک ، با نام کاربری خود وارد شوید یا ثبت نام کنید ]
مثال 5: کار کردن با یک type سفارشی
در این مثال، یک ایست type شده ایجاد می شود و سپس با استفاده از LINQ to Objects، کوئری می شود.
هدف از این مثال، تنها sort کردن لیست بخش ها به ترتیب شماره بخش ها می باشد. نتایج بازگشتی از این متد به شرح زیر است:کد:List<Parts> parts = new List<Parts>();
Parts p1 = new Parts();
p1.PartNumber = 1;
p1.PartDescription = "Cog";
parts.Add(p1);
Parts p2 = new Parts();
p2.PartNumber = 2;
p2.PartDescription = "Widget";
parts.Add(p2);
Parts p3 = new Parts();
p3.PartNumber = 3;
p3.PartDescription = "Gear";
parts.Add(p3);
Parts p4 = new Parts();
p4.PartNumber = 4;
p4.PartDescription = "Tank";
parts.Add(p4);
Parts p5 = new Parts();
p5.PartNumber = 5;
p5.PartDescription = "Piston";
parts.Add(p5);
Parts p6 = new Parts();
p6.PartNumber = 6;
p6.PartDescription = "Shaft";
parts.Add(p6);
Parts p7 = new Parts();
p7.PartNumber = 7;
p7.PartDescription = "Pulley";
parts.Add(p7);
Parts p8 = new Parts();
p8.PartNumber = 8;
p8.PartDescription = "Sprocket";
parts.Add(p8);
var list = from p in parts
orderby p.PartNumber ascending
select p;
StringBuilder sb = new StringBuilder();
foreach (Parts p in parts)
{
sb.Append(p.PartNumber + ": " + p.PartDescription +
Environment.NewLine);
}
MessageBox.Show(sb.ToString(), "Parts List");
شکل 9: کوئری Ordered Parts List
[ برای مشاهده لینک ، با نام کاربری خود وارد شوید یا ثبت نام کنید ]
کلاس parts استفاده شده در این type پشت لیست parts به شرح زیر است:
مثال 6: جستجوی یک List<T> با استفاده از LINQ to Objectsکد:public class Parts
{
private int mPartNumber;
private string mPartDescription;
public Parts()
{
}
public Parts(int partNum, string partDescr)
{
mPartNumber = partNum;
mPartDescription = partDescr;
}
public int PartNumber
{
get { return mPartNumber; }
set { mPartNumber = value; }
}
public string PartDescription
{
get { return mPartDescription; }
set { mPartDescription = value; }
}
}
در این مثال، یک لیست type شده ایجاد و populate می شود، و سپس با استفاده از LINQ to Objects کوئری می شود. در این مورد، کوئری شامل کلمه "where" است که فقط matchهایی را بازمیگرداند که با حرف "S" شروع می شوند"
شکل 10: نتایج کوئری Matching Partsکد:// only return parts starting with 'S'
var matchingParts = from m in parts
where m.PartDescription.StartsWith("S")
select m;
StringBuilder sb = new StringBuilder();
foreach (Parts p in matchingParts)
{
sb.Append(p.PartNumber + ": " + p.PartDescription +
Environment.NewLine);
}
MessageBox.Show(sb.ToString(), "Matching Parts List");
[ برای مشاهده لینک ، با نام کاربری خود وارد شوید یا ثبت نام کنید ]
مثال 7: جستجوی یک List<T> با استفاده از LINQ to Objects و بازگرداندن یک Single Result
در این مثال، یک لیست type شده ایجاد و populate می شود، و سپس با استفاده از LINQ to Objects کوئری می شود. در این مورد، یک single result از تایپ “Parts” را بازمی گرداند:
ممکن است کسی از روش زیر نیز برای بازگرداندن یک single value از query استفاده کند:کد:var matchingPart = (from m in parts
where m.PartNumber.Equals(5)
select m).Single<Parts>();
MessageBox.Show(matchingPart.PartDescription, "Matching Part");
هر دو روش یک نتیجه را می دهند، همانطور که در شکل زیر نشان داده شده.کد:var matchingPart = (from m in parts
where m.PartNumber.Equals(5)
select m.PartDescription).Single<String>();
MessageBox.Show(matchingPart, "Matching Part");
شکل 11: بازگرداندن یک Single Result
[ برای مشاهده لینک ، با نام کاربری خود وارد شوید یا ثبت نام کنید ]
هدف از مثال های بالا، مروری ساده بر چگونگی مدیریت queryهای پایه ای در collectionها با استفاده از LINQ to Objects بود؛ مسلماً عملیات های پیچیده تری نیز وجود دارند که می توان با استفاده از فرآیندهای مشابه اجرا کرد.
شروع
یک solution به این مقاله الصاق شده است که شامل یک پروژه Win Forms بنام "LinqToObjects" است؛ این پروژه شامل موارد زیر است:
• دو فرم، فرم اصلی (frmContactBook) و یک فرم که برای نمایش لیست کلی مخاطبین (frmFullList) بکار می رود؛
• یک کلاس قابل serialize بنام Contact، که برای دربرگرفتن داده های مرتبط بکار می رود؛
• یک کلاس بنام Serializer که شامل دو متد استاتیک است و برای serialize و deserialize کردن داده های مخاطبین استفاده می شود.
اگر پروژه الصاق شده را در Visual Studio 2008 باز کنید، در solution explorer پنجره زیر باید ظاهر شود:
شکل 12:
[ برای مشاهده لینک ، با نام کاربری خود وارد شوید یا ثبت نام کنید ]
کد: Contact.cs
کلاس Contact، کلاس دربرگیرنده است که برای ذخیره همه داده های مرتبط مخاطب که در برنامه استفاده می شود، بکار می رود. در حالیکه این demonstration، از داده های مخاطب استفاده می مند، اما می توان براحتی با چیز مفیدتری جایگزین کرد.
این کلاس با importهای نرمال و پیش فرض شروع می شود:
کد:using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
بخش بعدی شامل فضای نامی و تعریف کلاس می شود. توجه داشته باشید که کلاس بصورتی تعریف می شود که قابل serialize باشد، صفت قابل serialize نشان می دهد می نوان کلاس را میدهد می نوان کلاس را serialize کرد.
کد:namespace LinqToObjects
{
[Serializable]
public class Contact
{
قسمت تعریف شده در این کلاس، عضوهای داده ای (member variables) را که درون کلاس استفاده می شوند، نشان می دهد؛ هرعضو داده ای که در درون استفاده می شود، از طریق propertyهای عمومی قابل دسترس می شوند.
قسمت بعدی کد در این کلاس شامل constructorها می شود. دو constructor تعریف می شوند، یک constructor پیش فرض که کلاس نمونه جدید ایجاد می کند و به آن یک ID داخلی تخصیص می دهد. constructor دیگر،یک ID را به عنوان پارامتر ورودی قبول می کند و ID داخلی مخاطب را به آن value اختصاص می دهد.کد:#region Member Variables
private Guid mId;
private string mFirstName;
private string mMiddleName;
private string mLastName;
private string mStreet;
private string mCity;
private string mState;
private string mZip;
private string mEmail;
private string mHousePhone;
private string mWorkPhone;
private string mCellPhone;
private string mFax;
#endregion
آخرین بخش این کد در این کلاس، شامل propertyهای قسمت (region) است، این قسمت شامل همه propertyهایی می شود که برای دسترسی به عضوهای داده ای تعریف شده اند. توجه داشته باشید که از آنجاییکه مقدار ID همیشه توسط constructor مشخص می شود.کد:#region Constructor
public Contact()
{
mId = Guid.NewGuid();
}
public Contact(Guid ID)
{
mId = ID;
}
#endregion
کد: فرم اصلی برنامه (frmContactBook.cs)کد:#region Properties
public Guid ID
{
get
{
return mId;
}
}
public string FirstName
{
get
{
return mFirstName;
}
set
{
mFirstName = value;
}
}
public string MiddleName
{
get
{
return mMiddleName;
}
set
{
mMiddleName = value;
}
}
public string LastName
{
get
{
return mLastName;
}
set
{
mLastName = value;
}
}
public string Street
{
get
{
return mStreet;
}
set
{
mStreet = value;
}
}
public string City
{
get
{
return mCity;
}
set
{
mCity = value;
}
}
public string State
{
get
{
return mState;
}
set
{
mState = value;
}
}
public string ZipCode
{
get
{
return mZip;
}
set
{
mZip = value;
}
}
public string Email
{
get
{
return mEmail;
}
set
{
mEmail = value;
}
}
public string HousePhone
{
get
{
return mHousePhone;
}
set
{
mHousePhone = value;
}
}
public string WorkPhone
{
get
{
return mWorkPhone;
}
set
{
mWorkPhone = value;
}
}
public string CellPhone
{
get
{
return mCellPhone;
}
set
{
mCellPhone = value;
}
}
public string Fax
{
get
{
return mFax;
}
set
{
mFax = value;
}
}
#endregion
}
}
این بخش، فرم اصلی برنامه است، قسمت بیشتر کد، قالب کاری برنامه را فراهم می کند و ربطی به LINQ to Objects ندارد، اما کل کد در اینجا توضیح داده می شود تا context مناسب را در اختیار بگذارد. فرم اصلی برنامه contact، شامل کنترل های زیر است.
شکل 13: frmContactBook.cs
- Menu
- File
- New
- Open
- Save
- Save As
- Exit
- Contacts
- Add Contact
- Remove Contact
- List All Contacts
- Toolbar
- Add
- Remove
- Find by Last Name
- Save Data
- Navigate to Previous Contact
- Navigate to Next Bird Contact
- Exit Application
- Split Container Left Hand Side
- Alphabet List
- Alphabetized Names List
- Split Container Right Hand Side
- First name text box control
- Middle name text box control
- Last name text box control
- Street text box control
- City text box control
- State text box control
- Zip code text box control
- Home phone number text box control
- Work phone number text box control
- Cell number text box control
- Fax number text box control
- Email address text box control
[ برای مشاهده لینک ، با نام کاربری خود وارد شوید یا ثبت نام کنید ]
این کلاس با importهای نرمال و پیش فرض شروع می شود:
using System;
کد:using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
بخش بعدی شامل فضای نامی و تعاریف کلاس می شود.
قسمتی تعریف شده در این کلاس ، عضو داده ای را که در درون استفاده شده، تعریف می کند؛ هر عضو داده ای که در درون استفاده می شود، از طریق propertyهای عمومی، قابل دسترس می شوند. هر commentیی که کنار هر تعریف وجود دارد، منظورش را توضیح می دهد.کد:namespace LinqToObjects
{
public partial class frmContactBook : Form
{
قسمت بعدی کد این کلاس، شامل constructor می شود. هنگام شروع، برنامه یک لیستی جدید از اطلاعات مخاطبین، و شی اطلاعات مخاطبین ایجاد می کند، و سپس نشان دهنده موقعیت کنونی را روی صفر، و فرم کثیف Boolean را روی "false " تنظیم می کند.کد:#region Member Variables
List<Contact> contacts; // create a typed list of contacts
Contact currentContact; // create a single contact instance
int currentPosition; // used to hold current position
string currentFilePath; // file path to current contact file
bool dirtyForm; // keep track of dirty forms
#endregion
قسمت بعدی کد ‘Toolstrip Event Handlers’ نامیده می شود؛ اولین event handler در این قسمت، event handler کلیک دکمه Add است؛ این متد فقط منوی control’s click event handler را فراخوانی می کند و کد موجود در آن event handler، یک مخاطب جدید به داده های مخاطب فعلی اضافه می کند.کد:#region Constructor
/// <summary>
/// Constructor
/// </summary>
public frmContactBook()
{
InitializeComponent();
// initialize a new set of contact data
// in case the user is starting a new
// file; replaces this if the user
// opens an existing file
contacts = new List<Contact>();
currentContact = new Contact();
contacts.Add(currentContact);
currentPosition = 0;
dirtyForm = false;
}
#endregion
click event handler بعدی جهت خروج از برنامه، هنگامی که کاربر روی دکمه خروج toolstrip کلیک می کند، استفاده می شود؛ دوباره این handler فقط منوی click event handler را فراخوانی میکند.کد:#region Toolstrip Event Handlers
/// <summary>
/// Add a new contact
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void tsbAdd_Click(object sender, EventArgs e)
{
addToolStripMenuItem_Click(this, new EventArgs());
}
کد:/// <summary>
/// Exit the application
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void tsbExit_Click(object sender, EventArgs e)
{
exitToolStripMenuItem_Click(this, new EventArgs());
}
click event handler بعدی، فایل اطلاعات مخاطب فعلی را را هارد ذخیره می کند، دوباره این handler فقط منوی click event handler را فراخوانی می کند.کد:/// <summary>
/// Save the current contacts file
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void tsbSave_Click(object sender, EventArgs e)
{
saveStripMenuItem_Click(this, new EventArgs());
}
handler بعدی، مخاطب فعلی را از لیست اطلاعات مخاطبین حذف می کند؛ دوباره این handler فقط منوی click event handler را فراخوانی می کند.handler بعدی، برای جستجوی یک مخاطب براساس نام خانوادگی استفاده می شود. این کد از کوئری a LINQ to Objects استفاده می کند تا اولین نتیجه جستجو را پیدا کند. این handler از term text box control روی toolstrip استفاده می کند تا نام خانوادگی را capture کند و از دکمه search برای اجرای فرمان جستجو استفاده می کند. کد زیر برای توصیف آنچه که در این متد اتفاق می افتد، آورده شده است:کد:/// <summary>
/// Remove the current record
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void tsbRemoveRecord_Click(object sender, EventArgs e)
{
removeToolStripMenuItem_Click(this, new EventArgs());
}
کد:/// <summary>
/// Find a specific contact by the contact's
/// last name
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void tsbFindContact_Click(object sender, EventArgs e)
{
// return if the search term was not provided
if (String.IsNullOrEmpty(tspSearchTerm.Text))
{
MessageBox.Show("Enter a last name in the space proved.", "Missing
Search Term");
return;
}
try
{
// using linq to objects query to get first matching name
var foundGuy =
(from contact in contacts
where contact.LastName == tspSearchTerm.Text
select contact).FirstOrDefault<Contact>();
// set the current contact to the found contact
currentContact = foundGuy;
currentPosition = contacts.IndexOf(currentContact);
// update the display by loading the
// found contact
LoadCurrentContact();
// clear the search term textbox and return
tspSearchTerm.Text = string.Empty;
return;
}
catch
{
MessageBox.Show("No matches were found", "Search Complete");
}
}
handler بعدی، برای بازگرداندن مخاطبی از موقعیت فعلی مخاطب نمایش داده شده استفاده می شود.handler بعدی، برای جلو بردن از موقعیت کنونی مخاطب نمایش داده شده استفاده می شود.کد:/// <summary>
/// Navigate back to the previous record
/// if not at the lower limit
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void tsbNavBack_Click(object sender, EventArgs e)
{
// capture form changes and plug them
// into the current contact before
// navigating off the contact
SaveCurrentContact();
// don't exceed the left limit
if (currentPosition != 0)
{
currentPosition--;
currentContact = contacts[currentPosition];
LoadCurrentContact();
}
}
قسمت یعدی شامل منوی item click event handlers می شود. اولین menu item برای اضافه کردن مخاطبی جدید به لیست مخاظبین فعلی استفاده می شود. این متد، تابع SaveCurrentContact را فرامی خواند که هرگونه entry را که درحال حاضر به فرم ایجاد شده است را ذخیره می کند، و سپس نمون ای جدید از مخاطب ایجاد می کند، و مخاطبی جدید را به لیست مخاطبین اضافه می کند.کد:/// <summary>
/// Navigate to the next record if not at the
/// upper limit
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void tsbNavForward_Click(object sender, EventArgs e)
{
// capture form changes and plug them
// into the current contact before
// navigating off the contact
SaveCurrentContact();
// don't exceed the right limit
if (currentPosition < contacts.Count - 1)
{
currentPosition++;
currentContact = contacts[currentPosition];
LoadCurrentContact();
}
}
on
منوی بعدی item click event handler، یک لیست مخاطبین جدید ایجاد می کند؛ قبل از ایجاد لیست مخاطبین جدید، این handler چک می کند ببیند آیا فرم جاری dirty است تا به کاربر دیگر فرصت دهد قبل از اینکه لیست جاری را ببندد، آن را ذخیره کند. با پیروی از آن، لیست مخاطبین با لیستی جدید جایگزین می شود و کنترل های فرم پاک می شوند.کد:#region Menu Item Click Event Handler
/// <summary>
/// Add a new contact to the current
/// contact list and update the
/// display
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void addToolStripMenuItem_Click(object sender, EventArgs e)
{
SaveCurrentContact();
currentContact = new Contact();
contacts.Add(currentContact);
ClearScreen();
dirtyForm = true;
}
handler بعدی، برای بازکردن فایل مخاطب بکار می رود. این handler، یک فرو dirty را چک می کند و اگر فرم dirty باشد، فرصت ذخیره کردن را به کاربر میب دهد. یک متد open مجزا فراخوانی می شود تا عملیات واقعی باز کردن فایل را handle کند.کد:/// <summary>
/// Create a new contact file but check for
/// a dirty form first and allow the user to save
/// if the current data has changed but not
/// been saved.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void newToolStripMenuItem_Click(object sender, EventArgs e)
{
if (dirtyForm == true)
{
if (MessageBox.Show(this, "You have not saved the current contact
uld you like to save before starting a new " +
"contact database?", "Save Current Data",
MessageBoxButtons.YesNo) ==
System.Windows.Forms.DialogResult.Yes)
{
saveAsMenuItem_Click(this, new EventArgs());
}
else
{
// discard and start new document
contacts = new List<Contact>();
ClearScreen();
}
}
else
{
// start new document
contacts = new List<Contact>();
ClearScreen();
}
}
handler بعدی، بعد از اینکه دوباره چک کرد آیا فرم dirty است یا خیر، برنامه را می بنند.کد:/// <summary>
/// Open an existing contact data file but
/// first check for a dirty form and allow the
/// user the opportunity to save it before
/// leaving the current contact file
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void openToolStripMenuItem_Click(object sender, EventArgs e)
{
if (dirtyForm == true)
{
if (MessageBox.Show(this, "You have not saved the current contact
data; would you like to save before opening a different " +
"contact database?", "Save Current Data",
MessageBoxButtons.YesNo) ==
System.Windows.Forms.DialogResult.Yes)
{
saveAsMenuItem_Click(this, new EventArgs());
}
else
{
Open();
}
}
else
{
Open();
}
}
save menu item برای ذخیره فایل مخاطبین فعلی روی هارد بکار می رود؛ این تابع ابتدا یک SaveCurrentContact را فرامی خواند که برای ذخیره مخاطب فعلی در لیست اطلاعات مخاطبین جاری استفاده می شود. و اگر در حال حاضر هیچ نامی برای متغیر currentFilePath تعیین نشده باشد، این تابع از پنجره save file برای capture کردن نام یک فایل استفاده می کند، یا، اگر متغیر تعیین شده باشد، فایل را با استفاده از مسیر فایل ذخیره می کند. این فایل هنگام فراخوانی فایل serialize کردن، روی هارد ذخیره می شود.کد:/// <summary>
/// Exit the application but first check for
/// a dirty form and allow the user to save the file
/// before leaving the application
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void exitToolStripMenuItem_Click(object sender, EventArgs e)
{
if (dirtyForm == true)
{
if (MessageBox.Show(this, "You have not saved the current contact
data; would you like to save before exiting?", "Save Current
Data",
MessageBoxButtons.YesNo)==System.Windows.Forms.Dia logResult.Yes)
{
tsbSave_Click(this, new EventArgs());
}
else
{
Application.Exit();
}
}
else
{
Application.Exit();
}
}
قسمت بعدی کد جهت ساپورت آیتم منوی “Save As” بکار می رود، متد فراخوانی، شبیه متد قبلی save است، ولی مستقیماً پنجره Save File را باز می کند تا به کاربر اجازه نامگذاری فایل یا تغییر نام فایل را بدهد.کد:/// <summary>
/// Save the current file; if the file is
/// new, open the save file dialog, else
/// just save the existing file
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void saveStripMenuItem_Click(object sender, EventArgs e)
{
SaveCurrentContact();
if (String.IsNullOrEmpty(currentFilePath))
{
SaveFileDialog SaveFileDialog1 = new SaveFileDialog();
try
{
SaveFileDialog1.Title = "Save CON Document";
SaveFileDialog1.Filter = "CON Documents (*.con)|*.con";
if (SaveFileDialog1.ShowDialog() ==
System.Windows.Forms.DialogResult.Cancel)
{
return;
}
}
catch
{
return;
}
currentFilePath = SaveFileDialog1.FileName;
if (String.IsNullOrEmpty(currentFilePath))
{
return;
}
}
// persist the contacts file to disk
Serializer.Serialize(currentFilePath, contacts);
// tell the user the file was saved
MessageBox.Show("File " + currentFilePath + " saved.", "File Saved.");
// everything is saved, set the dirtyform
// boolean to false
dirtyForm = false;
}
کد:/// <summary>
/// Open the save file dialog to allow the user
/// to name or rename the current file and to save
/// the file with the new name
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void saveAsMenuItem_Click(object sender, EventArgs e)
{
SaveFileDialog SaveFileDialog1 = new SaveFileDialog();
try
{
SaveFileDialog1.Title = "Save CON Document";
SaveFileDialog1.Filter = "CON Documents (*.con)|*.con";
if (SaveFileDialog1.ShowDialog() ==
System.Windows.Forms.DialogResult.Cancel)
{
return;
}
}
catch
{
return;
}
currentFilePath = SaveFileDialog1.FileName;
if (String.IsNullOrEmpty(currentFilePath))
{
return;
}
// persist the contacts file to disk
Serializer.Serialize(currentFilePath, contacts);
// tell the user the file was saved
MessageBox.Show("File " + currentFilePath + " saved.", "File Saved.");
// everything is saved, set the dirtyform
// boolean to false
dirtyForm = false;
}
متد بعدی، مخاطب فعلی را از لیست مخاطبین حذف می کند و موقعیت مخاطب فعلی را آپدیت می کند.کد:/// <summary>
/// Delete the current contact and update
/// the display
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void removeToolStripMenuItem_Click(object sender, EventArgs e)
{
// make sure there are records
if (contacts.Count == 0)
{
// remove the current record
contacts.Remove(currentContact);
// check to see if the current
// position is at the limit
// and move up or down
// as required
if (currentPosition == 0)
currentPosition++;
else
currentPosition--;
// reload the current contact
// from the new position
currentContact = contacts[currentPosition];
LoadCurrentContact();
// dirty the form since a
// record was removed
dirtyForm = true;
}
}
متد بعدی برای برای مرتب کردن مخاطبین بر اساس حروف الفبا و ارسال لیست نتایج به نمونه ای از فرم که برای نمایش همه مخاطبین موجود در data grid view control بکار می رود، استفاده می شود. لیست مخاطبین فعلی بر اساس حروف الفبای نام خانوادگی، نام میانی، و نام کوچک مرتب می شود.
قسمت بعدی شامل یک garbage can collection از متدهایی است که در قسمتی بنام “Housekeeping” نگهداری می شود:کد:/// <summary>
/// Create an ordered list of contacts and display that list
/// in a datagrid
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void listAllContactsToolStripMenuItem_Click(object sender,
EventArgs e)
{
// use linq to objects to create a list of contacts
// ordered by the contact's last name, first name,
// and middle name
var orderedCons =
(from contact in contacts
orderby contact.LastName ascending,
contact.FirstName ascending,
contact.MiddleName ascending
select contact);
// create an instance of the full list form and pass it's
// constructor the list converted to a List<Contact>
frmFullList f = new frmFullList(orderedCons.ToList<Contact>());
f.Show();
}
on
#region Housekeeping
اولین متدی موجود در این بخش برای پاک کردن همه text box هایی استفاده می شود که برای نمایش اطلاعات مخاطب بکار می رود.
از متد بعدی برای بارگذاری اطلاعات موجود در مخاطب فعلی به کنترل هایی استفاده می شود که برای نمایش اطلاعات مخاطب بکار می رود.کد:/// <summary>
/// Clear all the values currently
/// held in the contact display
/// area's textboxes
/// </summary>
private void ClearScreen()
{
txtFirstName.Text = string.Empty;
txtMiddleName.Text = string.Empty;
txtLastName.Text = string.Empty;
txtStreet.Text = string.Empty;
txtCity.Text = string.Empty;
txtState.Text = string.Empty;
txtZipCode.Text = string.Empty;
txtHousePhone.Text = string.Empty;
txtWorkPhone.Text = string.Empty;
txtCellPhone.Text = string.Empty;
txtFax.Text = string.Empty;
txtEmailAddress.Text = string.Empty;
}
متد بعدی تمامی اطلاعاتی را که در حال حاضر روی فرم مخاطب فعلی قرار دارد capture می کند و آن را روی propertyهای مخاطب فعلی می نویسد. هرگاه یک مخاطب اغییر پیدا می کند، بطوری که همه ویرایش ها روی مخاطب موجود در لیست local باقی می ماند تا روی هارد نوشته شود، این متد فراخوانی می شود. بعلاوه، این متد ترتیب و لیست مخاطبین و مخاطب نمایش داده شده را update می کند.کد:/// <summary>
/// Display the current contact's
/// information in the contact
/// display area
/// </summary>
private void LoadCurrentContact()
{
// update the form fields
txtFirstName.Text = currentContact.FirstName;
txtMiddleName.Text = currentContact.MiddleName;
txtLastName.Text = currentContact.LastName;
txtStreet.Text = currentContact.Street;
txtCity.Text = currentContact.City;
txtState.Text = currentContact.State;
txtZipCode.Text = currentContact.ZipCode;
txtHousePhone.Text = currentContact.HousePhone;
txtWorkPhone.Text = currentContact.WorkPhone;
txtCellPhone.Text = currentContact.CellPhone;
txtFax.Text = currentContact.Fax;
txtEmailAddress.Text = currentContact.Email;
// display the current user in the status bar
tslViewWho.Text = "Now Viewing " +
txtFirstName.Text + " " + txtLastName.Text;
}
کد:/// <summary>
/// Save the current contacts information from the
/// textboxes contained in the contact display area
/// </summary>
private void SaveCurrentContact()
{
if (!String.IsNullOrEmpty(txtFirstName.Text) &&
(!String.IsNullOrEmpty(txtLastName.Text)))
{
try
{
// get all of the textbox values and
// plug them into the current contact object
currentContact.FirstName = txtFirstName.Text;
currentContact.MiddleName = txtMiddleName.Text;
currentContact.LastName = txtLastName.Text;
currentContact.Street = txtStreet.Text;
currentContact.City = txtCity.Text;
currentContact.State = txtState.Text;
currentContact.ZipCode = txtZipCode.Text;
currentContact.HousePhone = txtHousePhone.Text;
currentContact.WorkPhone = txtWorkPhone.Text;
currentContact.CellPhone = txtCellPhone.Text;
currentContact.Fax = txtFax.Text;
currentContact.Email = txtEmailAddress.Text;
// reorder the contacts by last, first, and
// middle name to keep everything in correct
// alphabetical order
var orderedContacts =
(from contact in contacts
orderby contact.LastName ascending,
contact.FirstName ascending,
contact.MiddleName ascending
select contact).ToList<Contact>();
// set the contacts list to the newly
// ordered list
contacts = orderedContacts;
// update the current position index value
currentPosition = contacts.IndexOf(currentContact);
// reload the current contact
LoadCurrentContact();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Error");
}
}
}
از متد بعدی برای باز کردن و deserialize کردن فایل مخاطب موجود استفاده می شود، وآن را برای ویرایش و مشاهده از درون برنامه آماده می کند.
کد:/// <summary>
/// Open a contacts file and display the contact
/// information in the application
/// </summary>
public void Open()
{
OpenFileDialog OpenFileDialog1 = new OpenFileDialog();
OpenFileDialog1.Title = "Open con Document";
OpenFileDialog1.Filter = "CON Documents (*.con)|*.con";
if (OpenFileDialog1.ShowDialog() ==
System.Windows.Forms.DialogResult.Cancel)
{
return;
}
currentFilePath = OpenFileDialog1.FileName;
if (String.IsNullOrEmpty(currentFilePath))
{
return;
}
if (System.IO.File.Exists(currentFilePath) == false)
{
return;
}
// deserialize file content into contacts
// list to make it available to the application
contacts = Serializer.Deserialize(currentFilePath);
// alphabetize the contact list
// by last, first, and middle name and
// push the results into a List<T>
var orderedContacts =
(from contact in contacts
orderby contact.LastName ascending,
contact.FirstName ascending,
contact.MiddleName ascending
select contact).ToList<Contact>();
// set the contacts to the ordered
// version of the contact list
contacts = orderedContacts;
// Load contacts at position zero
// if contacts list is not empty
if (contacts != null)
{
currentContact = contacts.ElementAt<Contact>(0);
LoadCurrentContact();
dirtyForm = false;
}
}
on
قسمت آخر در این کلاس فرم برای handle کردن eventهای کنترل listbox استفاده می شود. این کنترل ها برای فراهم کردن یک Rolodex از عملکردهای برنامه بکار می رود. کنترل های listbox در سمت چپ کترل پنل split بارگذاری می شوند. کنترل listbox تمامی حروف الفبا را، هنگامی که از کنترل listbox پایینی برای نمایش همه نام های خانوادگی که با حرفی که در listbox بالایی انتخاب شده استفاده می شود، نشان می دهد.
تابع اول، index انتخابی برای listbox بالایی که حاوی همه حروف الفبا است را handle می کند. وقتی یک حرف جدید انتخاب می شود، این متد از یک query ساده LINQ to Objects برای پیدا کردن همه مخاطبین با نام خانوادگی ای که با حرف انتخای شده شروع می شوند، استفاده می کند. سپس listbox پایینی پاک می شود و سپس مخاطبین یافته شده در یک string که نام خانوادگی، نام کوچک و نام میانی مخاطب را نشان می دهد، نمایش داده می شوند، و هر string نمایش داد ه شده به کنترل listbox پایینی اضافه می شود.کد:#region Listbox Event Handlers
کد:/// <summary>
/// Display matching contacts whose last name begins
/// with the letter selected from the alphabet
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void lstAlphas_SelectedIndexChanged(object sender, EventArgs e)
{
string alpha = lstAlphas.SelectedItem.ToString();
if (contacts != null)
{
try
{
// use linq to objects query to find
// last names matching the selected
// letter of the alphabet
var alphaGroup =
from contact in contacts
where contact.LastName.ToUpper().StartsWith(alpha)
select contact;
// clear out any names from the
// existing list
lstNames.Items.Clear();
// add the short list of matching
// names to the list box
foreach (var con in alphaGroup)
lstNames.Items.Add(con.LastName + ", " +
con.FirstName + " " + con.MiddleName);
// if not matches were found, tell the user
// with a note in the box
if (alphaGroup.Count<Contact>() < 1)
{
lstNames.Items.Clear();
lstNames.Items.Add("No matches were found");
}
}
catch
{
lstNames.Items.Clear();
lstNames.Items.Add("No matches were found");
}
}
}
رویداد selected index changed از لیست باکس names، در قسمت بعدی کد handle می شود. در آن، string نام (که نام خانوادگی، نام کوچک و نام میانی) parse می شود و در کوئری LINQ to Objects استفاده می شود تا لیستی از همه نام های پیدا شده را بازگرداند؛ اولین نام پیدا شده در فرم مخاطب نمایش داده می شود و موقعیت index برای ساپورت پیمایش لیست update می شود.
کد: frmFullList.csکد:/// <summary>
/// Find the matching contact for the name picked
/// from this list box and display that contact's
/// information in the contact display area
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void lstNames_SelectedIndexChanged(object sender, EventArgs e)
{
// if there were no matches found, return from this function
if (lstNames.SelectedItem.ToString().Trim() == "No matches were found")
return;
// variables to hold parts of the name as search terms
string first, middle, last;
// get the last name
string[] arr = lstNames.SelectedItem.ToString().Trim().Split(',') ;
last = arr[0].Trim();
// get the first name
string[] arr2 = arr[1].ToString().Trim().Split(' ');
first = arr2[0].Trim();
// get the middle name
middle = arr2[1].Trim();
// cannot complete the query without the three values
// so return if the information is missing
if (String.IsNullOrEmpty(last) ||
String.IsNullOrEmpty(first) ||
String.IsNullOrEmpty(middle))
{
MessageBox.Show("This query requires a first, middle, and a last
name.",
"Missing Name Values");
return;
}
try
{
// using linq to objects query to get a collection of matching names
// when all three names match
var foundGuy =
(from contact in contacts
where contact.FirstName.Equals(first) &&
contact.LastName.Equals(last) &&
contact.MiddleName.Equals(middle)
select contact).FirstOrDefault<Contact>();
// set the current contact to the first found
// contact
currentContact = foundGuy;
// update the index position used to maintain
// the current position within the list
currentPosition = contacts.IndexOf(currentContact);
// reload the current contact and return
LoadCurrentContact();
return;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Error Encountered");
}
}
on
کلاس فرم حاوی یک کنترل data grid view و یک constructor است که لیست مخاطب را به صورت پارامتر ورودی (argument) قبول می کند. هنگام شروع، لیست به کنترل data grid view محدود می شود. تغییرات اعمال شده توسط ویرایش ها در grid، در لیست مخاطبین نگهداری می شود.
کد:using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace LinqToObjects
{
public partial class frmFullList : Form
{
public frmFullList(List<Contact> cons)
{
InitializeComponent();
dgvFullList.DataSource = cons;
dgvFullList.Columns[0].Visible = false;
}
}
}
خلاصه مقاله
این مقاله تعدادی مثال ساده از کوئری های LINQ to Objects را نشان می دهد که در ساپورت یک برنامه ساده بکار می رود. ممکن است از LINQ to Objects برای generate کردن کوئری های پیچیده تری استفاده شود، اما کوئری هایی که در این مقاله نشان داده شدند، نشانگر بعضی از taskهای رایجی است که ممکن است در برنامه ای مشابه انجام شوند. اکثر کدها به صورت قالب کاری برای برنامه در اختیار شما قرار داده شدند، و برای ایجاد محیطی مفید برای تست کردن بعضی از کوئری های وابسته به LINQ to Objects ضروری بودند.