• توجه: در صورتی که از کاربران قدیمی ایران انجمن هستید و امکان ورود به سایت را ندارید، میتوانید با آیدی altin_admin@ در تلگرام تماس حاصل نمایید.

استفاده از LINQ to Objects در #C

parniann

کاربر ويژه
[h=4]مقدمه این مقاله، مقدمه ایست بر استفاده از queryهای LINQ to Objects جهت ساپورت یک برنامه windows forms. این مقاله، ساختار عبارات LINQ to Objects را مورد بحث قرار می دهد و توضیح می دهد چگونه می توان از LINQ to Objects بدون بافت (context) یک برنامه واقعی استفاده کرد.
پروژه ای که در این مقاله آورده شده است، یک contact manager ساده است که ممکن است برای capture کردن و ذخیره اطلاعات مخاطب های یک شخص در قالب دفتر تلفن، مورد استفاده قرار گیرد. این برنامه از LINQ to Objects برای مدیریت، query کردن و مرتب کردن لیست مخاطبین، استفاده می کند. همچنین این برنامه شامل یک فایل contact مبتدی با مجموعه ای از مخاطبین تستی است.





این برنامه دارای عملکردهای ریز است:

  • یک فایل اطلاعات مخاطبین ایجاد می کند.
  • مخاطبین را به فایل اطلاعات مخاطبین اضافه می کند.
  • مخاطبین را از فایل اطلاعات مخاطبین حذف می کند.
  • مخاطبین را بر اساس نام خانوادگی شان جستجو می کند.
  • جزییات مخاطبین را ایجاد و ویرایش می کند.
    • نام
    • نام خانوادگی
    • خیابان
    • شهر
    • ایالت
    • کد پستی
    • تلفن منزل
    • تلفن محل کار
    • تلفن همراه
    • آدرس ای میل

  • فایل اطلاعات مخاطبین را ذخیره می کند.
  • فایل اطلاعات مخاطبین را دوباره باز می کند.
  • همه مخاطبین را در فایل اطلاعات مخاطبین پیمایش می کند.
  • می توان لیستی از همه مخاطبین را در فایل اطلاعات مخاطبین مشاهده کرد.
  • امکان جستجو را بر اساس حرف آغازین نام خانوادگی فراهم می کند (Rolodex).
معمولاً، روش های مورد استفاده در این برنامه، نمایانگر یکی از چندین روشی انجام آن کارها است، مانند خیلی از چیزها در دنیای .Net، چندین جایگزین وجود دارد و اگر مایل باشید می توانید با استفاده از جایگزین های دیگر، کد را برای کارکردن با داده ها تغییر دهید.





[h=4]عبارات LINQ to Objects این بخش، بعضی از تکنیک های رایج و مورد استفاده در ساختار عبارات LINQ to Objects را مورد بحث قرار می دهد. به طور خلاصه، LINQ to Objects، ابزاری برای مدیریت queryهایی در collectionهای درون حافظه در اختیار برنامه نویسان قرار می دهد. این تکنیک ها که برای query کردن چنین collectionهایی استفاده می شوند، شبیه روش هایی هستند که برای مدیریت queryها در databaseهای رابطه ای استفاده می شوند، ولی ساده تر هستند.
[h=5]آناتومی عبارات 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:

var list = from t in tools
select t;


در این مثال، یک متغیر بی نوع (untyped variable) به نام "list" ایجاد می شود و همه آیتم های موجود در string array به این این object اضافه می شوند، این typeها هر کدام دارای معنای خاصی است، مثلاً "t" بمعنای tools است؛ از آنجاییکه "tools" یک string array است، framework استنباط می کند که "t" یک string نیز هست. البته، این موضوع آنقدرها هم افتضاح نیست، زیرا شما می توانید array را تکرار کنید تا لزوماً همان کار را انجام دهید؛ اما می توانید queryهای پیچیده تری را با LINQ to Objects ایجاد کنید.




مثال 2: Select با یک Where Clause
این مثال، یک کوئری LINQ to Objects را نشان می دهد که شامل یک where clause می شود. در این مثال، با مجموعه ای پرنده ها به شکل یک string array شروع می کنیم؛ از LINQ to Objects برای query کردن این string array استفاده می شود تا یک subset از array را به شکل همه پرندگانی که با حرف 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");


اگر قرار بود این query را اجرا کنیم، (همه پرندگانی که با 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");






مثال 4: generate کردن یک Ordered 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، کوئری می شود.

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");

هدف از این مثال، تنها sort کردن لیست بخش ها به ترتیب شماره بخش ها می باشد.





کلاس parts استفاده شده در این type پشت لیست parts به شرح زیر است:

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; }
}
}



مثال 6: جستجوی یک List<T> با استفاده از LINQ to Objects
در این مثال، یک لیست type شده ایجاد و populate می شود، و سپس با استفاده از LINQ to Objects کوئری می شود. در این مورد، کوئری شامل کلمه "where" است که فقط matchهایی را بازمیگرداند که با حرف "S" شروع می شوند"

// 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” را بازمی گرداند:

var matchingPart = (from m in parts
where m.PartNumber.Equals(5)
select m).Single<Parts>();

MessageBox.Show(matchingPart.PartDescription, "Matching Part");



ممکن است کسی از روش زیر نیز برای بازگرداندن یک single value از query استفاده کند:


var matchingPart = (from m in parts
where m.PartNumber.Equals(5)
select m.PartDescription).Single<String>();

MessageBox.Show(matchingPart, "Matching Part");



هر دو روش یک نتیجه را می دهند،





هدف از مثال های بالا، مروری ساده بر چگونگی مدیریت queryهای پایه ای در collectionها با استفاده از LINQ to Objects بود؛ مسلماً عملیات های پیچیده تری نیز وجود دارند که می توان با استفاده از فرآیندهای مشابه اجرا کرد.
 

parniann

کاربر ويژه
[h=4]شروع یک solution به این مقاله اضافه شده است که شامل یک پروژه Win Forms بنام "LinqToObjects" است؛ این پروژه شامل موارد زیر است:

· دو فرم، فرم اصلی (frmContactBook) و یک فرم که برای نمایش لیست کلی مخاطبین (frmFullList) بکار می رود؛
· یک کلاس قابل serialize بنام Contact، که برای دربرگرفتن داده های مرتبط بکار می رود؛
· یک کلاس بنام Serializer که شامل دو متد استاتیک است و برای serialize و deserialize کردن داده های مخاطبین استفاده می شود.

[h=4] کد: 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های عمومی قابل دسترس می شوند.

#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



قسمت بعدی کد در این کلاس شامل constructorها می شود. دو constructor تعریف می شوند، یک constructor پیش فرض که کلاس نمونه جدید ایجاد می کند و به آن یک ID داخلی تخصیص می دهد. constructor دیگر،یک ID را به عنوان پارامتر ورودی قبول می کند و ID داخلی مخاطب را به آن value اختصاص می دهد.

#region Constructor

public Contact()
{
mId = Guid.NewGuid();
}

public Contact(Guid ID)
{
mId = ID;
}

#endregion


آخرین بخش این کد در این کلاس، شامل propertyهای قسمت (region) است، این قسمت شامل همه propertyهایی می شود که برای دسترسی به عضوهای داده ای تعریف شده اند. توجه داشته باشید که از آنجاییکه مقدار ID همیشه توسط constructor مشخص می شود.

#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

}
}
 

parniann

کاربر ويژه
[h=4]کد: فرم اصلی برنامه (frmContactBook.cs) این بخش، فرم اصلی برنامه است، قسمت بیشتر کد، قالب کاری برنامه را فراهم می کند و ربطی به LINQ to Objects ندارد، اما کل کد در اینجا توضیح داده می شود تا context مناسب را در اختیار بگذارد. فرم اصلی برنامه contact، شامل کنترل های زیر است.

  • 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;



بخش بعدی شامل فضای نامی و تعاریف کلاس می شود.

namespace LinqToObjects
{

public partial class frmContactBook : Form
{



قسمتی تعریف شده در این کلاس ، عضو داده ای را که در درون استفاده شده، تعریف می کند؛ هر عضو داده ای که در درون استفاده می شود، از طریق propertyهای عمومی، قابل دسترس می شوند. هر commentیی که کنار هر تعریف وجود دارد، منظورش را توضیح می دهد.

#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



قسمت بعدی کد این کلاس، شامل constructor می شود. هنگام شروع، برنامه یک لیستی جدید از اطلاعات مخاطبین، و شی اطلاعات مخاطبین ایجاد می کند، و سپس نشان دهنده موقعیت کنونی را روی صفر، و فرم کثیف Boolean را روی "false " تنظیم می کند.

#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



قسمت بعدی کد ‘Toolstrip Event Handlers’ نامیده می شود؛ اولین event handler در این قسمت، event handler کلیک دکمه Add است؛ این متد فقط منوی control’s click event handler را فراخوانی می کند و کد موجود در آن 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());
}



click event handler بعدی جهت خروج از برنامه، هنگامی که کاربر روی دکمه خروج toolstrip کلیک می کند، استفاده می شود؛ دوباره این handler فقط منوی click event handler را فراخوانی می کند.


/// <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 را فراخوانی می کند.

/// <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());
}



handler بعدی، برای جستجوی یک مخاطب براساس نام خانوادگی استفاده می شود. این کد از کوئری a LINQ to Objects استفاده می کند تا اولین نتیجه جستجو را پیدا کند. این handler از term text box control روی toolstrip استفاده می کند تا نام خانوادگی را capture کند و از دکمه search برای اجرای فرمان جستجو استفاده می کند. کد زیر برای توصیف آنچه که در این متد اتفاق می افتد، آورده شده است:

/// <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 بعدی، برای بازگرداندن مخاطبی از موقعیت فعلی مخاطب نمایش داده شده استفاده می شود.

/// <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();
}
}



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

/// <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 handlers می شود. اولین menu item برای اضافه کردن مخاطبی جدید به لیست مخاطبین فعلی استفاده می شود. این متد، تابع SaveCurrentContact را فرامی خواند که هرگونه entry را که درحال حاضر به فرم ایجاد شده است را ذخیره می کند، و سپس نمودن ای جدید از مخاطب ایجاد می کند، و مخاطبی جدید را به لیست مخاطبین اضافه می کند.


#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;
}



منوی بعدی item click event handler، یک لیست مخاطبین جدید ایجاد می کند؛ قبل از ایجاد لیست مخاطبین جدید، این handler چک می کند ببیند آیا فرم جاری dirty است تا به کاربر دیگر فرصت دهد قبل از اینکه لیست جاری را ببندد، آن را ذخیره کند. با پیروی از آن، لیست مخاطبین با لیستی جدید جایگزین می شود و کنترل های فرم پاک می شوند.

/// <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 بعدی، برای بازکردن فایل مخاطب بکار می رود. این handler، یک فرو dirty را چک می کند و اگر فرم dirty باشد، فرصت ذخیره کردن را به کاربر می دهد. یک متد open مجزا فراخوانی می شود تا عملیات واقعی باز کردن فایل را handle کند.


/// <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();
}
}


handler بعدی، بعد از اینکه دوباره چک کرد آیا فرم dirty است یا خیر، برنامه را می بنند.

/// <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.DialogResult.Yes)
{
tsbSave_Click(this, new EventArgs());
}
else
{
Application.Exit();
}
}
else
{
Application.Exit();
}
}


save menu item برای ذخیره فایل مخاطبین فعلی روی هارد بکار می رود؛ این تابع ابتدا یک SaveCurrentContact را فرامی خواند که برای ذخیره مخاطب فعلی در لیست اطلاعات مخاطبین جاری استفاده می شود. و اگر در حال حاضر هیچ نامی برای متغیر currentFilePath تعیین نشده باشد، این تابع از پنجره save file برای capture کردن نام یک فایل استفاده می کند، یا، اگر متغیر تعیین شده باشد، فایل را با استفاده از مسیر فایل ذخیره می کند. این فایل هنگام فراخوانی فایل serialize کردن، روی هارد ذخیره می شود.

/// <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;
}


قسمت بعدی کد جهت ساپورت آیتم منوی “Save As” بکار می رود، متد فراخوانی، شبیه متد قبلی save است، ولی مستقیماً پنجره Save File را باز می کند تا به کاربر اجازه نامگذاری فایل یا تغییر نام فایل را بدهد.

/// <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 بکار می رود، استفاده می شود. لیست مخاطبین فعلی بر اساس حروف الفبای نام خانوادگی، نام میانی، و نام کوچک مرتب می شود.

/// <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


قسمت بعدی شامل یک garbage can collection از متدهایی است که در قسمتی بنام “Housekeeping” نگهداری می شود:

#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;
}
از متد بعدی برای بارگذاری اطلاعات موجود در مخاطب فعلی به کنترل هایی استفاده می شود که برای نمایش اطلاعات مخاطب بکار می رود.

/// <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;
}

متد بعدی تمامی اطلاعاتی را که در حال حاضر روی فرم مخاطب فعلی قرار دارد capture می کند و آن را روی propertyهای مخاطب فعلی می نویسد. هرگاه یک مخاطب اغییر پیدا می کند، بطوری که همه ویرایش ها روی مخاطب موجود در لیست local باقی می ماند تا روی هارد نوشته شود، این متد فراخوانی می شود. بعلاوه، این متد ترتیب و لیست مخاطبین و مخاطب نمایش داده شده را update می کند.


/// <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 بالایی انتخاب شده استفاده می شود، نشان می دهد.


#region Listbox Event Handlers

تابع اول، index انتخابی برای listbox بالایی که حاوی همه حروف الفبا است را handle می کند. وقتی یک حرف جدید انتخاب می شود، این متد از یک query ساده LINQ to Objects برای پیدا کردن همه مخاطبین با نام خانوادگی ای که با حرف انتخای شده شروع می شوند، استفاده می کند. سپس listbox پایینی پاک می شود و سپس مخاطبین یافته شده در یک string که نام خانوادگی، نام کوچک و نام میانی مخاطب را نشان می دهد، نمایش داده می شوند، و هر string نمایش داد ه شده به کنترل listbox پایینی اضافه می شود.


/// <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 می شود.

/// <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
 

parniann

کاربر ويژه
[h=4]کد: frmFullList.cs کلاس فرم حاوی یک کنترل 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;
}
}
}


[h=4]خلاصه مقاله این مقاله تعدادی مثال ساده از کوئری های LINQ to Objects را نشان می دهد که در ساپورت یک برنامه ساده بکار می رود. ممکن است از LINQ to Objects برای generate کردن کوئری های پیچیده تری استفاده شود، اما کوئری هایی که در این مقاله نشان داده شدند، نشانگر بعضی از taskهای رایجی است که ممکن است در برنامه ای مشابه انجام شوند. اکثر کدها به صورت قالب کاری برای برنامه در اختیار شما قرار داده شدند، و برای ایجاد محیطی مفید برای تست کردن بعضی از کوئری های وابسته به LINQ to Objects ضروری بودند.
 

parniann

کاربر ويژه
ایجاد یک DataTable از یک Query

Data binding، یکی از کاربردهای رایج شی DataTable است. متد CopyToDataTable، خروجی های یک query را می گیرد و داده ها را در DataTable کپی می کند، و سپس می توان برای data binding استفاده کرد. وقتی عملیات های داده ها اجرا شود، DataTable جدید دوباره با DataTable منبع ادغام می شود. متد CopyToDataTable از فرآیند زیر برای ایجاد یک DataTable از یک query استفاده می مند.

  1. متد CopyToDataTable، یک DataTable را از منبع جدول کپی برداری می کند (یک شی DataTable که اینترفیس IQueryable<T> را پیاده سازی می کند). منبع IEnumerable عموماً از یک عبارت LINQ to DataSet یا query متد نشات می گیرد.
  2. Schemaی DataTable کپی برداری شده، از ستون های شی DataRow شمارش شده در جدول منبع ساخته می شود، و نام جدول کپی برداری شده، نام جدول منبع است که کلمه "query" به آن ضمیمه شده است.
  3. برای هر ردیف در جدول منبع، محتویات ردیف به یک شی DataRow جدید کپی می شود، و سپس به جدول کپی برداری شده، وارد می شود. Propertyهای RowState و RowError، در طول عملیات کپی کردن، بدون تغییر باقی می مانند. اگر اشیاء DataRow در منبع از جدولی دیگر باشند، یک ArgumentException، ایجاد می شود.
  4. بعد از اینکه همه اشیاء DataRow در جدول خروجی قابل query کپی شوند، DataTable کپی شده بازگردانده می شود. اگر sequence اصلی، حاوی هیچ شی DataRow نباشد، متد یک DataTable خالی بازمی گرداند.
توجه داشته باشید که فراخوانی متد CopyToDataTable، باعث خواهد شد query برای اجرا شدن به جدول منبع بچسبد.
وقتی که متد CopyToDataTable با یک مرجع null یا value type قابل null در یک ردیف در جدول منبع روبرو می شود، . در این روش، valueهای null، در DataTable بازگردانده شده، بدرستی مدیریت می شوند.
نکته: متد CopyToDataTable، به صورت یک خروجی که می تواند ردیف ها را از اشیاء DataTable یا DataSet بازگرداند، پذیرفته می شود. متد CopyToDataTable، داده ها را کپی می کند، اما propertyها را از اشیاء DataTable یا DataSet اصلی به DataTable بازگردانده شده کپی نمی کند. شما باید propertyها، از قبیل Locale و TableName را در DataTable بازگردانده شده مستقیماً تنظیم کنید.
مثال زیر، جدول SalesOrderHeader table را برای سفارش های بعد 8 آگوست 2001، query می کند، و از متد CopyToDataTable برای ایجاد یک DataTable از آن query استفاده می کند. سپس DataTable به یک BindingSource می چسبد که به عنوان یک ****** برای یک DataGridView عمل می کند.
در VB:

' Bind the System.Windows.Forms.DataGridView object
' to the System.Windows.Forms.BindingSource object.
dataGridView.DataSource = bindingSource

' Fill the DataSet.
Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim orders As DataTable = ds.Tables("SalesOrderHeader")

' Query the SalesOrderHeader table for orders placed
' after August 8, 2001.
Dim query = _
From order In orders.AsEnumerable() _
Where order.Field(Of DateTime)("OrderDate") > New DateTime(2001, 8, 1) _
Select order

' Create a table from the query.
Dim boundTable As DataTable = query.CopyToDataTable()

' Bind the table to a System.Windows.Forms.BindingSource object,
' which acts as a proxy for a System.Windows.Forms.DataGridView object.
bindingSource.DataSource = boundTable

در #C:

// Bind the System.Windows.Forms.DataGridView object
// to the System.Windows.Forms.BindingSource object.
dataGridView.DataSource = bindingSource;

// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable orders = ds.Tables["SalesOrderHeader"];

// Query the SalesOrderHeader table for orders placed
// after August 8, 2001.
IEnumerable<DataRow> query =
from order in orders.AsEnumerable()
where order.Field<DateTime>("OrderDate") > new DateTime(2001, 8, 1)
select order;

// Create a table from the query.
DataTable boundTable = query.CopyToDataTable<DataRow>();

// Bind the table to a System.Windows.Forms.BindingSource object,
// which acts as a proxy for a System.Windows.Forms.DataGridView object.
bindingSource.DataSource = boundTable;
 

parniann

کاربر ويژه
[h=4]ایجاد یک متد CopyToDataTable<T> سفارشی متدهایCopyToDataTable کنونی، فقط روی یک منبع IEnumerable<T>، یعنی جاییکه پارامتر ژنریک T، نوعی DataRow محسوب می شود، عمل می کند. گرچه این موضوع مفید است، اما به جداول اجازه نمی دهد از یک sequence انواع عددی (scalar) و queryهایی که انواع ناشناس را بازمی گردانند، یا از queryهایی که پیوند جداول را اجرا می کنند ایجاد شود.

در VB:

Public Class Item
Private _Id As Int32
Private _Price As Double
Private _Genre As String

Public Property Id() As Int32
Get
Return Id
End Get
Set(ByVal value As Int32)
_Id = value
End Set
End Property

Public Property Price() As Double
Get
Return _Price
End Get
Set(ByVal value As Double)
_Price = value
End Set
End Property

Public Property Genre() As String
Get
Return _Genre
End Get
Set(ByVal value As String)
_Genre = value
End Set
End Property

End Class
Public Class Book
Inherits Item
Private _Author As String
Public Property Author() As String
Get
Return _Author
End Get
Set(ByVal value As String)
_Author = value
End Set
End Property
End Class

Public Class Movie
Inherits Item
Private _Director As String
Public Property Director() As String
Get
Return _Director
End Get
Set(ByVal value As String)
_Director = value
End Set
End Property

End Class

در #C:

public class Item
{
public int Id { get; set; }
public double Price { get; set; }
public string Genre { get; set; }
}

public class Book : Item
{
public string Author { get; set; }
}

public class Movie : Item
{
public string Director { get; set; }
}
[h=4]مثال این مثال، یک پیوند بین جداول SalesOrderHeader و SalesOrderDetail را اجرا می کند تا سفارش های آنلاین ماه آگوست را بدست بیاورد و جدولی را از query ایجاد می کند.
در VB:


' Fill the DataSet.
Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim orders As DataTable = ds.Tables("SalesOrderHeader")
Dim details As DataTable = ds.Tables("SalesOrderDetail")


Dim query = _
From order In orders.AsEnumerable() _
Join detail In details.AsEnumerable() _
On order.Field(Of Integer)("SalesOrderID") Equals _
detail.Field(Of Integer)("SalesOrderID") _
Where order.Field(Of Boolean)("OnlineOrderFlag") = True And _
order.Field(Of DateTime)("OrderDate").Month = 8 _
Select New With _
{ _
.SalesOrderID = order.Field(Of Integer)("SalesOrderID"), _
.SalesOrderDetailID = detail.Field(Of Integer)("SalesOrderDetailID"), _
.OrderDate = order.Field(Of DateTime)("OrderDate"), _
.ProductID = detail.Field(Of Integer)("ProductID") _
}

Dim table As DataTable = query.CopyToDataTable()



در #C:

// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable orders = ds.Tables["SalesOrderHeader"];
DataTable details = ds.Tables["SalesOrderDetail"];

var query =
from order in orders.AsEnumerable()
join detail in details.AsEnumerable()
on order.Field<int>("SalesOrderID") equals
detail.Field<int>("SalesOrderID")
where order.Field<bool>("OnlineOrderFlag") == true
&& order.Field<DateTime>("OrderDate").Month == 8
select new
{
SalesOrderID =
order.Field<int>("SalesOrderID"),
SalesOrderDetailID =
detail.Field<int>("SalesOrderDetailID"),
OrderDate =
order.Field<DateTime>("OrderDate"),
ProductID =
detail.Field<int>("ProductID")
};

DataTable orderTable = query.CopyToDataTable();


[h=4]مثال مثال زیر، یک collection برای آیتمهای قیمتهای بالاتر از 9.99 دلار query می کند و یک جدول از خروجی های query ایجاد می کند.
در VB:

Dim book1 As New Book()
book1.Id = 1
book1.Price = 13.5
book1.Genre = "Comedy"
book1.Author = "Gustavo Achong"

Dim book2 As New Book
book2.Id = 2
book2.Price = 8.5
book2.Genre = "Drama"
book2.Author = "Jessie Zeng"

Dim movie1 As New Movie
movie1.Id = 1
movie1.Price = 22.99
movie1.Genre = "Comedy"
movie1.Director = "Marissa Barnes"

Dim movie2 As New Movie
movie2.Id = 1
movie2.Price = 13.4
movie2.Genre = "Action"
movie2.Director = "Emmanuel Fernandez"

Dim items(3) As Item
items(0) = book1
items(1) = book2
items(2) = movie1
items(3) = movie2

' Query for items with price greater than 9.99.
Dim query = From i In items _
Where i.Price > 9.99 _
Order By i.Price _
Select New With {i.Price, i.Genre}

Dim table As DataTable
table = query.CopyToDataTable()

در #C:

// Create a sequence.
Item[] items = new Item[]
{ new Book{Id = 1, Price = 13.50, Genre = "Comedy", Author = "Gustavo Achong"},
new Book{Id = 2, Price = 8.50, Genre = "Drama", Author = "Jessie Zeng"},
new Movie{Id = 1, Price = 22.99, Genre = "Comedy", Director = "Marissa Barnes"},
new Movie{Id = 1, Price = 13.40, Genre = "Action", Director = "Emmanuel Fernandez"}};

// Query for items with price greater than 9.99.
var query = from i in items
where i.Price > 9.99
orderby i.Price
select i;

// Load the query results into new DataTable.
DataTable table = query.CopyToDataTable();

[h=4]مثال مثال زیر، یک collection برای آیتمهای قیمتهای بالاتر از 9.99 دلار query می کند و خروجی ها رانمایش می دهد. Sequence بازگردانده شده انواع ناشناس در یک جدول موجود بارگذاری می کند.

در VB:

Dim book1 As New Book()
book1.Id = 1
book1.Price = 13.5
book1.Genre = "Comedy"
book1.Author = "Gustavo Achong"

Dim book2 As New Book
book2.Id = 2
book2.Price = 8.5
book2.Genre = "Drama"
book2.Author = "Jessie Zeng"

Dim movie1 As New Movie
movie1.Id = 1
movie1.Price = 22.99
movie1.Genre = "Comedy"
movie1.Director = "Marissa Barnes"

Dim movie2 As New Movie
movie2.Id = 1
movie2.Price = 13.4
movie2.Genre = "Action"
movie2.Director = "Emmanuel Fernandez"

Dim items(3) As Item
items(0) = book1
items(1) = book2
items(2) = movie1
items(3) = movie2

' Create a table with a schema that matches that of the query results.
Dim table As DataTable = New DataTable()
table.Columns.Add("Price", GetType(Integer))
table.Columns.Add("Genre", GetType(String))

' Query for items with price greater than 9.99.
Dim query = From i In items _
Where i.Price > 9.99 _
Order By i.Price _
Select New With {i.Price, i.Genre}

query.CopyToDataTable(table, LoadOption.PreserveChanges)

در #C:





// Create a sequence.
Item[] items = new Item[]
{ new Book{Id = 1, Price = 13.50, Genre = "Comedy", Author = "Gustavo Achong"},
new Book{Id = 2, Price = 8.50, Genre = "Drama", Author = "Jessie Zeng"},
new Movie{Id = 1, Price = 22.99, Genre = "Comedy", Director = "Marissa Barnes"},
new Movie{Id = 1, Price = 13.40, Genre = "Action", Director = "Emmanuel Fernandez"}};

// Create a table with a schema that matches that of the query results.
DataTable table = new DataTable();
table.Columns.Add("Price", typeof(int));
table.Columns.Add("Genre", typeof(string));

var query = from i in items
where i.Price > 9.99
orderby i.Price
select new { i.Price, i.Genre };

query.CopyToDataTable(table, LoadOption.PreserveChanges);

[h=4]مثال مثال زیر، یک collection برای آیتمهای قیمتهای بالاتر از 9.99 دلار query می کند و خروجی ها رانمایش می دهد. Sequence بازگردانده شده انواع ناشناس در یک جدول موجود بارگذاری می کند. Schemaی جدول به طور اتوماتیک بسط پیدا می کند زیرا انواع Book و Movies از نوع Item مشتق می شوند.
در VB:

Dim book1 As New Book()
book1.Id = 1
book1.Price = 13.5
book1.Genre = "Comedy"
book1.Author = "Gustavo Achong"

Dim book2 As New Book
book2.Id = 2
book2.Price = 8.5
book2.Genre = "Drama"
book2.Author = "Jessie Zeng"

Dim movie1 As New Movie
movie1.Id = 1
movie1.Price = 22.99
movie1.Genre = "Comedy"
movie1.Director = "Marissa Barnes"

Dim movie2 As New Movie
movie2.Id = 1
movie2.Price = 13.4
movie2.Genre = "Action"
movie2.Director = "Emmanuel Fernandez"

Dim items(3) As Item
items(0) = book1
items(1) = book2
items(2) = movie1
items(3) = movie2

' Load into an existing DataTable, expand the schema and
' autogenerate a new Id.
Dim table As DataTable = New DataTable()
Dim dc As DataColumn = table.Columns.Add("NewId", GetType(Integer))
dc.AutoIncrement = True
table.Columns.Add("ExtraColumn", GetType(String))

Dim query = From i In items _
Where i.Price > 9.99 _
Order By i.Price _
Select New With {i.Price, i.Genre}

query.CopyToDataTable(table, LoadOption.PreserveChanges)

در #C:

// Create a sequence.
Item[] items = new Item[]
{ new Book{Id = 1, Price = 13.50, Genre = "Comedy", Author = "Gustavo Achong"},
new Book{Id = 2, Price = 8.50, Genre = "Drama", Author = "Jessie Zeng"},
new Movie{Id = 1, Price = 22.99, Genre = "Comedy", Director = "Marissa Barnes"},
new Movie{Id = 1, Price = 13.40, Genre = "Action", Director = "Emmanuel Fernandez"}};

// Load into an existing DataTable, expand the schema and
// autogenerate a new Id.
DataTable table = new DataTable();
DataColumn dc = table.Columns.Add("NewId", typeof(int));
dc.AutoIncrement = true;
table.Columns.Add("ExtraColumn", typeof(string));

var query = from i in items
where i.Price > 9.99
orderby i.Price
select new { i.Price, i.Genre };

query.CopyToDataTable(table, LoadOption.PreserveChanges);


[h=4]مثال مثال زیر، یک collection برای آیتمهای قیمتهای بالاتر از 9.99 دلار query می کند و sequence دوبل (double) را باز می گرداند، و در یک جدول جدید بارگذاری می شود.
در VB:

Dim book1 As New Book()
book1.Id = 1
book1.Price = 13.5
book1.Genre = "Comedy"
book1.Author = "Gustavo Achong"

Dim book2 As New Book
book2.Id = 2
book2.Price = 8.5
book2.Genre = "Drama"
book2.Author = "Jessie Zeng"

Dim movie1 As New Movie
movie1.Id = 1
movie1.Price = 22.99
movie1.Genre = "Comedy"
movie1.Director = "Marissa Barnes"

Dim movie2 As New Movie
movie2.Id = 1
movie2.Price = 13.4
movie2.Genre = "Action"
movie2.Director = "Emmanuel Fernandez"

Dim items(3) As Item
items(0) = book1
items(1) = book2
items(2) = movie1
items(3) = movie2

Dim query = From i In items _
Where i.Price > 9.99 _
Order By i.Price _
Select i.Price

Dim table As DataTable
table = query.CopyToDataTable()


در #C:

// Create a sequence.
Item[] items = new Item[]
{ new Book{Id = 1, Price = 13.50, Genre = "Comedy", Author = "Gustavo Achong"},
new Book{Id = 2, Price = 8.50, Genre = "Drama", Author = "Jessie Zeng"},
new Movie{Id = 1, Price = 22.99, Genre = "Comedy", Director = "Marissa Barnes"},
new Movie{Id = 1, Price = 13.40, Genre = "Action", Director = "Emmanuel Fernandez"}};

// load sequence of scalars.
IEnumerable<double> query = from i in items
where i.Price > 9.99
orderby i.Price
select i.Price;

DataTable table = query.CopyToDataTable();
 

parniann

کاربر ويژه
معرفی امکانات و قابلیت های LINQ برای مدیران پروژه ها

LINQ to SQL به برنامه نویسان .NET اجازه نوشتن queryها را درزبان .NET می دهد تا بتوانند داده ها را از بانک اطلاعاتیSQL Server بازیابی و تغییر دهند. به طور عام، LINQ to SQL اجازه ایجاد queryهای SQL در syntax زبان.NET انتخابی مان و کار کردن با مجموعه ای قوی از اشیا به عنوان نتیجه برگشتی را به ما می دهد. می توان تغییراتی را در این اشیا بوجود آورد و سپس دوباره آنها را در database ذخیره کرد. برای درک مفهوم syntax در LINQ to SQL، ما از schemaی بانک اطلاعاتی SQL استفاده می کنیم که نرم افزاری ساده برای ثبت محصولات و helpdesk است؛ و با داده های نمونه populate شده و دارای روابط کلید خارجی (foreign-key relationship) است که در جای مناسب تعریف می شود.

[h=3] SQL Database Schemaکه برای مثالهای LINQ to SQL استفاده می شود:
از شما می خواهم چند دقیقه این واقعیت را فراموش کنید که ما برضد نوع داده ای HookedOnLINQ کد نویسی می کنیم، بعداً توضیح خواهم داد که چگونه آن را در چند صفحه ایجاد کردم، فعلاً مد نظر داشته باشید که این، یک ساختار شی است که از این database schema تقلید می کند.


HookedOnLINQ db =
new HookedOnLINQ("Data Source=(local);Initial Catalog=HookedOnLINQ");

var q = from c in db.Contact
where c.DateOfBirth.AddYears(35) > DateTime.Now
orderby c.DateOfBirth descending
select c;

foreach(var c in q)
Console.WriteLine("{0} {1} b.{2}",
c.FirstName.Trim(),
c.LastName.Trim(),c.DateOfBirth.ToString("dd-MMM-yyyy"));


:Output​
Mack Kamph b.17-Sep-1977​
Armando Valdes b.09-Dec-1973​


عبارت LINQ to SQL Query در بانک اطلاعاتی SQL Server مخاطبان کمتر از 35 سال سن، ابتدا جوانترین.
هنگامی که حلقه foreach را وارد می کنیم، عبارت SQL زیر توسط LINQ فرموله می شود و روی سرور اجرا می شود. (دانستن این نکته مهم است که SQL فقط اولین باری که ما داده ها را request می کنیم اجرا می شود، تا آن موقع، query به صورت یک عبارت در حافظه نگهداری می شود این فرآیند، Deferred Execution نامیده می شود).


SELECT [t0].[ContactId], [t0].[FirstName], [t0].[LastName], [t0].[DateOfBirth],
[t0].[Phone], [t0]., [t0].[State]
FROM [Contact] AS [t0]
WHERE DATEADD(YEAR, @p0, [t0].[DateOfBirth]) > @p1
ORDER BY [t0].[DateOfBirth] DESC



[B]عبارات [/B][B]SQL[/B][B] که توسط [/B][B]LINQ[/B][B] ایجاد شده و مخاطبین بیشتر از تاریخی معین شده را که به عنوان یک پارامتر ارسال شده، باز می گرداند.[/B]
عبارت query در C#، به کد SQL پارامتریزه شده تبدیل شد، پارامترها ایجاد شدند و query روی سرور اجرا شد. LINQ to SQL به برنامه نویسان اجازه می دهد به استفاده از stored procedureها به جای SQL ادامه دهند، گرچه حالا مجبورید خودتان کد stored procedure را بنویسید، و در نتیجه بعضی از قابلیت های LINQ را از دست می دهید. بعداً در مورد این موضوع بیشتر بحث می کنیم، فعلاً در نظر داشته باشید که LINQ to SQL ازstored procedure و همچنین از فراخوانی های SQL که به طور دینامیکی ایجاد شده اند، در هر شرایطی ساپورت می کند.
اگر بانک اطلاعاتی شما دارای روابط کلید خارجی است، آنگاه سلسله مراتب آنها در مدلهای شی ایجاد شده منعکس می شود. می توان از طریق تعیین کردن جدول child به داده های رکوردهای مربوط دسترسی پیدا کنید. مثال بعدی نشان می دهد چگونه می توان در زنجیره روابط کلید خارج، بدون یک عبارت Join به طور مستقیم navigate کرد.



HookedOnLINQ db =
new HookedOnLINQ("Data Source=(local);Initial Catalog=HookedOnLINQ");

var q = from o in db.Orders
where o.Products.ProductName.StartsWith("Asset") &&
o.PaymentApproved == true
select new { name = o.Contacts.FirstName + " " +
o.Contacts.LastName,
product = o.Products.ProductName,
version = o.Products.Version +
(o.Products.SubVersion * 0.1)
};

foreach(var x in q)
Console.WriteLine("{0} - {1} v{2}",
x.name, x.product, x.version);

[LEFT]Output:[/LEFT] [LEFT]Barney Gottshall - Asset Blaster v1[/LEFT] [LEFT]Barney Gottshall - Asset Blaster v1.1[/LEFT] [LEFT]Armando Valdes - Asset Blaster Pro v1[/LEFT] [LEFT]Jeffery Deane - Asset Blaster Pro v1.1[/LEFT] [LEFT]Stewart Kagel - Asset Blaster Pro v1.1[/LEFT] [LEFT]Blaine Reifsteck - Asset Blaster Pro v1.1[/LEFT] [LEFT]Ariel Hazelgrove - Asset Blaster v1.1[/LEFT] [LEFT] [/LEFT] [B]دسترسی به روابط کلید خارجی ساده است. نیازی به join syntax نیست، مستقیماً به زیر اعضا (sub-members) دسترسی دارید.[/B]
این مدل شی سلسله مراتبی (hierarchical)، برای آپدیت ها نیز جواب می دهد. می توانید رکوردها را بوسیله تغییر داده اشیا و اضافه یا حذف کردن اشیا از جداول، در جداول مربوطه assign، اضافه یا حذف کنید. در پشت پرده، LINQ to SQL، فرمان SQL query زیر را ایجاد و آنرا اجرا می کند. از این نتایج برای populate کردن مجموعه شی نتیجه (result object collection) استفاده کرد که مجموعه ای از یک type ناشناس (Anonymous) است.

SELECT ([t2].[FirstName] + @p2) + [t2].[LastName] AS [value],
[t1].[ProductName], [t1].[Version] + ([t1].[SubVersion] * @p3) AS [value2]
FROM [Orders] AS [t0], [Products] AS [t1], [Contacts] AS [t2]
WHERE ([t2].[ContactId] = [t0].[ContactId]) AND
([t1].[ProductName] LIKE @p0) AND ([t0].[PaymentApproved] = @p1)
AND ([t1].[Product_Id] = [t0].[ProductId])



[B]کد [/B][B]SQL[/B][B] که نشان می دهد چگونه [/B][B]joins to related table through foreign-keys[/B][B]، اضافه شدند.[/B]
اگر بانک اطلاعاتی شما دارای روابط کلید خارجی نیست که بین دو جدول تعریف می شوند، LINQ to SQL، دسترسی نسبی (relational access) را توسط تعیین Joinها در عبارات query، قبول می کند. Query زیر چگونگی join کردن را در جاییکه یک کلید خارجی بین دو جدول Contacts.Phone و CallLogs.Number تعریف نشده، نشان می دهد.

HookedOnLINQ db =
new HookedOnLINQ("Data Source=(local);Initial Catalog=HookedOnLINQ");

var q = from call in db.CallLogs
join contact in db.Contacts on call.Number equals contact.Phone
select new {contact.FirstName, contact.LastName,
call.When, call.Duration};

foreach(var call in q)
Console.WriteLine("{0} - {1} {2} ({3}min)",
call.When.ToString("ddMMM HH:mm"),
call.FirstName.Trim(), call.LastName.Trim(), call.Duration);



[B]اگر هیچ کلید خارجی وجود نداشته باشد، می توانید از اپراتور [/B][B]Join[/B][B] در عبارت [/B][B]query[/B][B] استفاده کنید.[/B]
جهت تغییر دادن و اضافه کردن رکورد به بانک اطلاعاتی مان، فقط باید تغییراتی رابه اشیای موجود در حافظه اعمال کنید و سپس متد SubmitChanges را فراخوانی کنید (مواظب باشید، من یک بار به اشتباه متد AcceptChanges را فرا خواندم که تغییرات را قبول می کند و همه رکوردها را به صورت اریجینال mark می کند اما در database ذخیره نمی کند. LINQ to SQL، رد تغییرات را نگه می دارد و عبارات SQL را ایجاد می کند تا همه آپدیت ها، insertها، و deleteها را تحت تاثیر قرار دهد. می توانید این رفتار پیش فرض را Override کنید و متدهای پیاده سازی خودتان را تعیین کنید و به جای آن استفاده کنید. LINQ to SQL، یک تراکنش را در اطراف آپدیت های database ایجاد می کند، پس اگر قسمتی دچار اشکال شود، فرصت دارید تا error را capture کنید، آنرا اصلاح و دوباره تلاش کنید. همچنین می توانید کنترل کنید LINQ to SQL چگونه errorهای همزمان را مدیریت کند (وقتی شخص دیگری داده هایی را که قبلاً ویرایش می کردید تغییر می دهد، شما شانس ذخیره کردن را دارید).

HookedOnLINQ db =
new HookedOnLINQ("Data Source=(local);Initial Catalog=HookedOnLINQ");

// Change - Get an object, make the change in memory, Call SubmitChanges
Contacts q = (from c in db.Contacts
where c.FirstName == "Armando" && c.LastName == "Valdes"
select c).FirstOrDefault();

if (q != null) {
q.Email = "Armando.Valdes@aspiring-technology.com";
}

try {
db.SubmitChanges();
}
catch (OptimisticConcurrencyException e) {
// You have your choice of RefreshMode to resolve concurrency conflicts.
// You can KeepChanges, KeepCurrentValues, OverwriteCurrentValues.
e.Resolve(RefreshMode.OverwriteCurrentValues);
db.SubmitChanges();
}



[B]آپدیتی که چگونگی مدیریت کردن [/B][B]error[/B][B]های همزمان را نشان می دهد. شما تغییرات را در اشیا ایجاد می کند و سپس [/B][B]SubmitChanges[/B][B] را فرا می خوانید. [/B]
[B]Insert[/B] کردن رکوردهای جدید به سادگی ایجاد نمونه جدیدی از اشیا و اضافه کردن آن به مجموعه ی مناسب و سپس فراخوانی SubmitChanges است.


HookedOnLINQ db =
new HookedOnLINQ("Data Source=(local);Initial Catalog=HookedOnLINQ");

// Adding Records – (1) Create a new object and sub-objects,
// (2) Add it to the DataContext collection, (3) Call SubmitChanges

// (1)
Contacts newContact = new Contacts();
newContact.FirstName = "Troy";
newContact.LastName = "Magennis";
newContact.Phone = "425 749 0494";
newContact.Email = "troy@aspiring-technology.com";
newContact.DateOfBirth = new DateTime(1980, 08, 07);
// Create sub-record and add to this contact
Orders newOrder = new Orders();
newOrder.Products = (from p in db.Products
where p.ProductName == "Asset Blaster Pro"
select p).FirstOrDefault();
newOrder.DateOfPurchase = DateTime.Now;

// (2)
newContact.Orders.Add(newOrder);
db.Contacts.Add(newContact);

// (3)
db.SubmitChanges();



[B]insert[/B][B] کردن یک رکورد جدید و یک زیر رکورد مرتبط. فقط اشیا را ایجاد کنید و به یک [/B][B]collection[/B][B] اضافه کنید. [/B]
LINQ to SQL در SubmitChanges، عبارات SQL را به ترتیب صحیحی generate می کند تا رکوردهای جدید را در database ذخیره کند و هر کدام را به طور صحیح reference کند. در این مثال، LINQ to SQL، به insert کردن Contact جدید نیاز دارد تا ابتدا کلید اصلی را بدست بیاورد و سپس آنرا هنگام نوشتن ترتیب جدید در database استفاده کند. کل فرآیند دریک transaction انجام می شود، پس اگر هر مرحله ای دچار اشکال شود، آنگاه کل database به حالتی که قبل از اینکه SubmitChanges فراخوانده شود، بر میگردد.


Start LOCAL Transaction (ReadCommitted)

INSERT INTO [Contacts](FirstName, LastName, DateOfBirth, Phone, Email, State)
VALUES(@p0, @p1, @p2, @p3, @p4, @p5)
SELECT [t0].[ContactId]
FROM [Contacts] AS [t0]
WHERE [t0].[ContactId] = (CONVERT(Int,@ @IDENTITY))

INSERT INTO [Orders](ContactId, ProductId, DateOfPurchase, PaymentApproved, Quantity, Discount, AccessCode)
VALUES(@p0, @p1, @p2, @p3, @p4, @p5, @p6)
SELECT [t0].[OrderId]
FROM [Orders] AS [t0]
WHERE [t0].[OrderId] = (CONVERT(Int,@ @IDENTITY))

Commit LOCAL Transaction


[B]SQL[/B][B] هنگام نوشتن یک رکورد و زیر رکورد، اجرا می شود. به [/B][B]wrap[/B][B] کردن کل فرآیند توسط [/B][B]transaction[/B][B] دقت کنید. [/B]
این رکوردها بعد از اینکه متد SubmitChanges فراخوانی شود، اضافه می شوند.




حذف کردن رکوردها بسیار ساده است. می توانید یک شی را از مجموعه اشیایی که در حال حاضر در حافظه هستند و از query قبلی جمع آوری شده اند، حذف کنید.


// Delete the record(s) we just created (do sub-items first)
db.Orders.Remove(newOrder);
db.Contacts.Remove(newContact);
db.SubmitChanges();



[B]مثال هایی ازحذف کردن رکوردها از [/B][B]database[/B]
تا اینجا من یک مرحله مهم را حذف کرده ام. ما queryهایی را ضد یک type بنام HookedOnLINQ نوشته ایم که با یک database connection string، و instance types Contacts، و Orders، و Products شروع شده است. این type، از لنگر LINQ to SQL، کلاسی به نام DataContext، ارث می برد. این کلاس، marshalling عبارات query را به عبارات SQL ، و همچنین change tracking را در فراخوانی SubmitChanges مدیریت می کند. به علاوه، ما به داشتن typeهایی برای نمایش جداول داده هایمان و جنبه های mapping اشیا و روابط با معادل های SQLشان و بالعکس، نیاز داریم. گرچه همه این کلاسها را می توان به طور دستی ایجاد کرد، اما این کار اصلاً توصیه نمی شود. یک ساپورت زمان طراحی و درونی در Visual Studio به همراه یک ابزار خط فرمان (command line tool) وجود دارد که کل کارهای سنگین در code generation به جای ما انجام می دهد.
کلاس DataContext سفارشی:
· از System.Data.DLINQ.DataContext type ارث می برد
· مجموعه ای از instance type را آغاز می کند (Table<[type]>) و آنها را قابل دسترس می کند. (مثلاً، می توانیم db.Contacts را از میان عبارات query فرابخوانیم)
کلاس های instance object سفارشی:
· با یک صفت [Table] تغییر شکل می دهد
· حاوی fieldهای رایج یا propertyهایی است که با صفات [Column] تغییر شکل داده اند
· روابط کلید خارجی با صفت [Association] را تعریف می کنند
· رفتار Update، Insert، و Delete را با تعریف متدهایی که با صفات [Update]، [Insert]، و [Delete] علامتگذاری (mark) شده اند را override می کند
· Store Procedure، View، و Function wrapperها را با متدهایی که با صفات [StoredProcedure]، [View]، یا [Function] علامتگذاری (mark) شده اند را تعریف می کند
· تضمین می کند که رویدادهای PropertyChanging و PropertyChanged، هروقت که value تغییر می کند، روی می دهند.
گزینه هایی برای generate کردن کلاس های wrapper و مشتق DataContext، که عملکرد LINQ to SQL را روی جداول و اشیای databaseهای دیگر قبول می کنند، در زیر آورده شده:
· کل کار را به طور دستی انجام دهیم؛
· از built-in designer برای Visual Studio 2005 استفاده کنیم؛
· از ابزار خط فرمان SQLMetal استفاده کنیم؛
· از یک فایل XML mapping برای لینک کردن جداول و ستونهای database به typeها و propertyها استفاده کنیم. این کار به database و تغییرات mapping، اجازه می دهد در recompile کردن یک برنامه روی دهند.
برای generateکردن object wrapper برای بانک اطلاعاتی نمونه، بنام HookedOnLINQ، باید برنامه SqlMetal را با استفاده از ابزار خط فرمان و با argumentهای زیر اجرا کنید:
sqlmetal /server:(local) /database:HookedOnLINQ /code:HookedOnLINQ.cs
این کار، یک HookedOnLINQ.cs ایجاد می کند که برای تمامی مثال هایی که تا الان آورده شده اند، کاملاً کاربردی است. من فقط آن را درون پروژه اصلی کپی کردم و solution را compile کردم.
built-in designer به شما اجازه ایجاد یک DLINQ Object surface را می دهد. می توانید table instanceها را از پنجره Server Explorer، روی آن surface درگ کنید.روابط کلید خارجی به طور اتوماتیک به surface اضافه می شوند، اگر در بانک اطلاعاتی تعریف شوند، یا می توانید به طور دستی آنها را از Toolbox اضافه کنید. هنگام compile کردن، DataContext و instance typeها برای شما ایجاد می شوند.



[B]LINQ to SQL Designer Surface[/B][B]. درگ کردن جدول ها از [/B][B]server explorer[/B][B] باعث ایجاد [/B][B]object model[/B][B] می شود و به طور اتوماتیک روابط را تعریف می کند.[/B]
متد جایگزین استفاده از صفات که مدل رابطه ای را به مدل شی ای لینک می کند، منتقل کردن mappingها به یک فایل XML است. ابزار خط فرمان SQLMetal، این فایل XML را برای شما ایجاد می کند، اما می توانید generate کردن آنرا هرطور که دوست دارید اتوماتیک کنید. وقتی که DataContext را ایجاد می کنید، می توانید mapping XML را ارسال کنید، و این کار دقیقاً همان تاثیر استفاده از صفات را خواهد داشت، غیر از اینکه، هنگام compile کردن، به برنامه شما hardcode نمی شود.
خیلی ها معتقدند که database access همیشه باید از طریق Stored Procedure اجرا شوند تا امنیت را ارتقا دهند. LINQ to SQL به طور کامل Stored Procedureها را برای فراخوانی های عمومی و عملیات های update، insert، و delete، ساپورت می کند؛ و در موارد زیادی، تجربه شما را با خلاص کردن شما از ایجاد پارامترهای ورودی توسط دست، افزایش می دهد. اما، استفاده محض از Stored Procedureها، مزایای نوشتن عبارت های Query را در زبان برنامه نویسی اصلی برنامه نویس، از بین می برد. می توان از Stored Procedureها برای همه عملیات های Insert، Update، و Delete بکار برد و از عبارات Query برای بازیابی داده ها استفاده کرد. این کار باعث محافظت database در برابر از بین رفتن داده ها می شود، و به برنامه نویسان اجازه می دهد عبارات Query را در VB یا C# بسازند.
فراخوانی stored procedureها بسیار راحت شده. هنگام استفاده از ADO.NET، مجبور بودید قبل از ایجاد یک connection به database، و فراخوانی واقعی stored procedureها، پارامترهایی را به طور دستی بسازید. ابزار generate کردن، به عنوان بخشی از LINQ to SQL، تابع های wrapper را برای stored procedureها ایجاد می کند.
کد stored procedure زیر، لیستی از پرداختهای سررسیده را بازیابی می کند. روزهایی که از موعد پرداخت گذشته، به عنوان یک پارامتر ارسال می شود. نتیجه این کار، یک cursor با تعدادی ستون است، که یقیناً آن type نیست.


ALTER PROCEDURE [dbo].[GetOverdueAccounts]
@daysOverdue int = 15
AS
BEGIN
SET NOCOUNT ON;

SELECT o_OrderId, o.Quantity, o.DateOfPurchase, o.Discount,
c.FirstName + ' ' + c.LastName AS CustomerName,
c.Phone, c.Email,
p.ProductName, p.Price,
((p.Price*o.Quantity)*((100-o.Discount)/100)) AS Cost,
DATEDIFF(day, o.DateOfPurchase, GETDATE()) AS OverdueDays
FROM Orders o,
Contacts c,
Products p
WHERE o.ContactId = c.ContactId
AND o.ProductId = p.Product_Id
AND o.PaymentApproved = 0
AND p.IsBeta = 0
AND DATEADD(day, @daysOverdue, o.DateOfPurchase) < GETDATE()

END



ابزار generate کردن کد، دارای یک switch است که wrapper و result type را برای stored procedureها، generate می کند.


sqlmetal /server:(local) /database:HookedOnLINQ /sprocs /code:HookedOnLINQ.cs

HookedOnLINQ db =
new HookedOnLINQ("Data Source=(local);Initial Catalog=HookedOnLINQ");

var overdue = db.GetOverdueAccounts(30);

foreach (GetOverdueAccountsResult c in overdue)
Console.WriteLine("{0} days - {1:c}: {2}",
c.OverdueDays, c.Cost, c.CustomerName);

[LEFT]Output:[/LEFT] [LEFT]215 days - $300.00: Armando Valdes[/LEFT] [LEFT]30 days - $180.00: Adam Gauwain[/LEFT] [LEFT]30 days - $247.50: Adam Gauwain[/LEFT]
 

parniann

کاربر ويژه
مقدمه
شرکت ماکروسافت، Language Integrated Query یا LINQ را همراه با.NET Framework 3.5 معرفی کرد. LINQ، برنامه نویسان را قادر به query کردن منبع داده ها با استفاده از یک query مانند syntax با C# و VB.NET می کند. این منبع داده ها می توانند collectionها، بانک های اطلاعاتی SQL Server، XML، و datasetها باشند. به غیر از آنچه که از طرف Microsoft تامین می شود، LINQ گستردنی هم هست. این بدین معناست که شما می توانید منابع داده ها را فراتر از آنچه که مایکروسافت ship میکند، query کنید. مثال هایی از چنین پیاده سازی هایی عبارتند از LINQ To Flickr، LINQ To Amazon، LINQ to Google، و غیره. در این مقاله نشان می دهم چگونه می توان از LINQ To SQL جهت اجرای عملیات های CRUD روی یک بانک اطلاعاتی SQL Server استفاده کرد. من از بانک اطلاعاتی Northwind استفاده می کنم و یک برنامه ASP.NET می سازم تا قابلیت های LINQ To SQL را نشان دهم.

ابزار های لازم برای این مقاله:

1. Visual Studio 2008
2. .NET Framework 3.5
3. SQL Server 2005
ساختار راه حل (Solution Structure)

برای این مقاله، به دو پروژه نیاز داریم. یکی data layer است که generate خواهیم کرد، و دیگری یک برنامه تحت وب ASP.NET است. ساختار راه حل در Solution Explorer، شبیه مثال زیر است.


ایجاد Data Layer

قبل از اینکه data layer را generate کنیم، باید یک connection در Server Explorer ایجاد کنیم که به بانک اطلاعاتی Northwind اشاره می کند.


حالا data layerمان را با استفاده LINQ To SQL ایجاد می کنیم. برای انجام این کار، باید یک item جدید به پروژه data layer از نوع کلاسهای LINQ to SQL اضافه کنیم. نام آن را Northwind می گذاریم، همانگونه که در زیر نشان داده شده.


بعد از اضافه کردن یک کلاس LINQ to SQL، با یک designer surface روبرو می شویم. دراینجا تنها جداول را drag می کنیم که بخشی از data layer خواهد شد. در این مقاله، ما همه جداول را از طریق انتخاب کردن همه آنها در یک حرکت، روی designer، drag می کنیم. Designer ما باید بعد از drag کردن همه جداول روی آن شبیه شکل زیر باشد.


حالا باید solution را بسازیم تا مطمئن شویم همه چیز ok است. ما با موفقیت data layer مان را نصب کرده ایم. در Solution Explorer می بینم که دو فایل جدید داریم، یعنی Northwind.dbml.layout و Northwind.designer.cs. همچنین می بینیم که referenceهای لازم برای complie و run کردن کدمان نیز توسط Visual Studio اضافه شده اند.


فایل .cs حاوی کدی برای data layer ماست. بیایید کدی را که برای ما ایجاد شده، امتحان کنیم. نگاهی به کلاس Region خواهیم انداخت.

[Table(Name="dbo.Region")]
public partial class Region : INotifyPropertyChanging, INotifyPropertyChanged



این کلاس با صفت Table، آمیخته شده و برای خصوصیت Name، نام واقعی جدولی که در بانک اطلاعاتی مان موجود است، تخصیص داده شده. کلاس Region نیز interfaceهای InotifyPropertyChangingو INotifyPropertyChanged را پیاده سازی می کند. این interfaceها برای databinding بکار می روند. کلاس Region همچنین حاوی یک خصوصیت (property)برای هر ستون است. بیایید نگاهی به خصوصیت RegionDescription بیاندازیم.


[Column(Storage="_RegionDescription", DbType="NChar(50) NOT NULL",
CanBeNull=false)]
public string RegionDescription
{
get
{
return this._RegionDescription;
}
set
{
if ((this._RegionDescription != value))
{
this.OnRegionDescriptionChanging(value);
this.SendPropertyChanging();
this._RegionDescription = value;
this.SendPropertyChanged("RegionDescription");
this.OnRegionDescriptionChanged();
}
}
}



ستون ها با صفت Column آمیخته شده اند و valueها برای storasge ارسال می شوند، DbType و CanBeNull نشان می دهند آیا ستون null است یا خیر.
استفاده از Data Layer

حالا که data layer را ایجاد کرده ایم، روی برنامه های تحت وب ASP.NET کار خواهیم کرد، یعنی جاییکه data layer را خواهیم دید. در ابتدا یک web form ایجاد می کنیم تا مشتری ها را جستجو کنیم و نتایج را نمایش دهیم. همچنین یک web form ایجاد می کنیم تا مشتری های جدید را وارد کنیم. بیایید با web form برای جستجوی مشتری ها شروع کنیم؛ برای این کار، از صفحه Default.aspx استفاده خواهیم کرد. تعداد کمی کنترل روی web form قرارمی دهیم. این کنترل ها، پارامترهای جستجو و یک دکمه در اختیار ما می گذارد که وقتی رویش کلیک کنیم، جستجو را انجام می دهند و نتایج را نمایش می دهد.



یک کنترل GridView روی فرم مان قرار می دهیم تا نتایج جستجو را نمایش دهد. حالا مقداری کد در event handler کلیک دکمه قرار می دهیم تا جستجو را انجام دهد و نتایج را در GridView نمایش دهد. باید مطمئن شوید که یک reference به پروژه Data Layer، System.Data.Linq و عبارت مناسب وجود دارد. در زیر آنچه که event handler کلیک دکمه دربرخواهد گرفت، آورده شده است:



protected void buttonSearch_Click(object sender, EventArgs e)
{
using (NorthwindDataContext context = new NorthwindDataContext())
{
var customers =
from c in context.Customers
select c;
gridViewCustomers.DataSource = customers;
gridViewCustomers.DataBind();
}
}



این کد، جدول مشتریان را در بانک اطلاعاتی northwind، query می کند و همه مشتری ها را باز می گرداند. حالا باید کمی آن را modify کنیم تا نام مشتری ها و نام شرکت ها را به عنوان پارانترهایی برای queryمان قبول کند. event handler بعد از modify کردن به صورت زیر خواهد بود:


protected void buttonSearch_Click(object sender, EventArgs e)
{
using (NorthwindDataContext context = new NorthwindDataContext())
{
var customers =
from c in context.Customers
where (
c.ContactName.Contains(textBoxCustomerName.Text.Trim())
&&
c.CompanyName.Contains(textBoxCompanyName.Text.Trim()))
select c;
gridViewCustomers.DataSource = customers;
gridViewCustomers.DataBind();
}
}



حالا نتایج جستجو ***** می شود.
حالا بیایید یک فرم data entry برای مشتری ها ایجاد کنیم. باید یک web form جدید در پروژه ASP.NET مان وارد کنیم و آن را CustomerEntry بنامیم. برای شروع باید مطمئن شویم که فرم ما حاوی fieldهای لازم برای وارد کردن یک مشتری جدید است. فرم ما بعد از تکمیل شبیه نمونه شکل یر خواهد بود.


انتظار ما این است که هنگامی که روی دکمه Save Customer کلیک می کنیم، یک ردیف (row) جدید به جدول مشتری ها اضافه شود. کد زیر این کار را برای ما انجام می دهد.

protected void buttonSave_Click(object sender, EventArgs e)
{
using (NorthwindDataContext context = new NorthwindDataContext())
{
Customer customer = new Customer
{
CustomerID = textBoxCustomerID.Text,
CompanyName = textBoxCompanyName.Text,
ContactName = textBoxCustomerName.Text,
ContactTitle = textBoxTitle.Text,
Address = textBoxAddress.Text,
City = textBoxCity.Text,
Region = textBoxRegion.Text,
PostalCode = textBoxPostalCode.Text,
Country = textBoxCountry.Text,
Phone = textBoxPhone.Text,
Fax = textBoxFax.Text
};
context.Customers.InsertOnSubmit(customer);
context.SubmitChanges();
}
}


می توان یک ستون که از قبل در بانک اطلاعاتی موجود است را ابتدا توسط بازیابی داده ها و سپس توسط submit کردن آن از طریق DataContext، آپدیت کرد.
نتیجه گیری

در این مقاله، ما فقط یک عبارت SQL مستقل ننوشتیم تا داده ها را به یک بانک اطلاعاتی وارد کنیم یا آن را بازیابی کنیم. این، زیبایی LINQ To SQL است. به علاوه، کد بازیابی ما هنگامی که در C# است بسیار شبیه یک query است. باید از مزایای چنین روش موثر و یکپارچه ای برای کار کردن با داده ها قدردانی کرد.
 
بالا