PDA

View Full Version : آموزش: جستجوی پیشرفته در Entity FrameWork (مقایسه جستجو در دیتابیس و فیلتر اطلاعات از قبل لود شده)



Mahmoud.Afrad
دوشنبه 16 مرداد 1391, 12:25 عصر
با سلام

در این پست برنامه جستجوی پیشرفته توسط LINQ در Entity FrameWork را قرار میدم.

جدولی به نام Person با فیلدهای زیر را در نظر بگیرید
[ID] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](50) NULL,
[Lname] [nvarchar](50) NULL,
[Genre] NULL

دو تکست باکس به نامهای txtName , txtLastName برای نام و نام خانوادگی و یک چک باکس برای شرکت دادن و یا عدم شرکت دادن جنسیت در جستجو میباشد. دو رادیو باتن برای نوع جنسیت.

تمام اطلاعات مورد نیاز را توسط لیست قابل شمارشی از اشیاء Person به گریدویو اول بایند میکنیم:

IEnumerable<Person> allRecords;

private void Form1_Load(object sender, EventArgs e)
{
using (Database1Entities context = new Database1Entities())
{
allRecords = context.Person.ToList();
dgvAll.DataSource = allRecords;

if (dgvAll.Columns.Contains("Genre"))
dgvAll.Columns["Genre"].Visible = false;
}
}

(allRecords را در سطح کلاس تعریف کردیم تا برای فیلتر کردن بتوانیم از این لیست استفاده کنیم)


روش اول:
انجام جستجو روی دیتابیس:
برای جستجو کردن براساس انتخاب هایی که توسط کاربر انجام می شود که باید در جستجو شرکت داده شوند، با if انتخاب این پارامترها را بررسی می کنیم و در صورتی که توسط کاربر برای جستجو انتخاب و پر شده باشند شرط را روی کوئری اعمال میکنیم.

private void searchOnDataBase()
{
using (Database1Entities context = new Database1Entities())
{
// do search on database (execute query in database)
var persons = context.Person.AsQueryable();

if (txtName.Text.Trim() != string.Empty)
{
persons = persons.Where(p => p.Name.Contains(txtName.Text.Trim()));
}
if (txtLastName.Text.Trim() != string.Empty)
{
persons = persons.Where(p => p.Lname.Contains(txtLastName.Text.Trim()));
}
if (checkBox1.Checked && rdbMale.Checked)
{
persons = persons.Where(p => p.Genre == true);
}
else if (checkBox1.Checked && rdbFemale.Checked)
{
persons = persons.Where(p => p.Genre == false);
}

dgv_SearchOnDB.DataSource = persons;
}
}


کد بالا مستلزم اجرای کوئری در دیتابیس است و برای فیلتر کردن دیتابیس حجیم مناسب نیست. اما میتوان همان لیست قبلی را فیلتر کرد با کدی که در روش دوم می آید.

روش دوم:
فیلتر کردن لیست اطلاعات اولیه (بدون کوئری زدن روی دیتابیس انجام می شود چون اطلاعات یکبار لود شده اند)
(allRecords را به این دلیل در سطح کلاس تعریف کردیم تا در این متد قابل دسترس باشد):

private void filterList()
{
var persons = allRecords;

// do filtering (filter allrecords to result)
if (txtName.Text.Trim() != string.Empty)
{
persons = persons.Where(p => p.Name.Contains(txtName.Text.Trim()));
}

if (txtLastName.Text.Trim() != string.Empty)
{
persons = persons.Where(p => p.Lname.Contains(txtLastName.Text.Trim()));
}

if (checkBox1.Checked && rdbMale.Checked)
{
persons = persons.Where(p => p.Genre == true);
}
else if (checkBox1.Checked && rdbFemale.Checked)
{
persons = persons.Where(p => p.Genre == false);
}
// end filtering

dgv_FilteringRecords.DataSource = persons.ToList();
}

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

Database1Entities مدل EF ساخته شده از دیتابیس است.

نمونه زیر پروژه ای از کدهای بالاست با مقایسه زمانی اجرای کدها.

Helpco
پنج شنبه 10 خرداد 1397, 12:35 عصر
شرمنده پست قدیمی میارم بالا
من تبق این روش
کوئری میزنم


persons = persons.Where(p => p.Lname.Contains(txtLastName.Text.Trim()));


حالا اگه
p.Lname
ما خودش شامل چند ایتم باشه که با کارکتر (,) از هم جدا شده باشه این روش جواب نمیده و ایتم های که فقط بصورت تکی هست میاره و ایتم هاس که دوتای یا چند تای باشن داخا دیتابیس را نمیاره


حالا چطوی باید اونهام نمایش بدیم

danialafshari
پنج شنبه 10 خرداد 1397, 15:58 عصر
شرمنده پست قدیمی میارم بالا
من تبق این روش
کوئری میزنم


persons = persons.Where(p => p.Lname.Contains(txtLastName.Text.Trim()));


حالا اگه
p.Lname
ما خودش شامل چند ایتم باشه که با کارکتر (,) از هم جدا شده باشه این روش جواب نمیده و ایتم های که فقط بصورت تکی هست میاره و ایتم هاس که دوتای یا چند تای باشن داخا دیتابیس را نمیاره


حالا چطوی باید اونهام نمایش بدیم

با سلام
قبل از قرار دادن کد در اینجا قبلش توی نوت پد کپی کنید سپس از نوت پد در اینجا درون تگ C#‎‎‎‎‎‎ قرار بدید تا متنش به هم نریزه
منظورتون رو درست متوجه نشدم دوتایی و تکی یعنی چی؟ مثال بزنید؟ منظورتون چند شرط در Where هست؟ میتونید از عملگر ها استفاده کنید
مثال:

persons = persons.Where(p => p.Lname.Contains(txtLastName.Text.Trim()) && p.nationalCode.Contains(txtNationalCode.Text));

یا منظورتون فیلدهایی هست که به عنوان کلید خارجی هستند! اگر NavigationProperty رو درست پیاده سازی کرده باشید نباید مشکلی پیش بیاد
موفق باشید

ali_md110
پنج شنبه 10 خرداد 1397, 17:44 عصر
توجه داشته باشید لود اولیه اطلاعات درون رم برای جداول بزرگ هزینه بر هست

Helpco
شنبه 12 خرداد 1397, 08:50 صبح
با سلام
قبل از قرار دادن کد در اینجا قبلش توی نوت پد کپی کنید سپس از نوت پد در اینجا درون تگ C#‎‎‎‎‎‎‎ قرار بدید تا متنش به هم نریزه
منظورتون رو درست متوجه نشدم دوتایی و تکی یعنی چی؟ مثال بزنید؟ منظورتون چند شرط در Where هست؟ میتونید از عملگر ها استفاده کنید
مثال:

persons = persons.Where(p => p.Lname.Contains(txtLastName.Text.Trim()) && p.nationalCode.Contains(txtNationalCode.Text));

یا منظورتون فیلدهایی هست که به عنوان کلید خارجی هستند! اگر NavigationProperty رو درست پیاده سازی کرده باشید نباید مشکلی پیش بیاد
موفق باشید

ببنید داخل دیتا بیس فیلد Lname شامل (محمد,رضا,مهدی) داخل یک ردیف ردیف دوم شامل (حسین,حسن)ردیف سوم (محمد) و الی اخر
خوب حلا ما یک تکس منوسیم محمد وقتی اجرا مگریم فقط ردیف سوم میاره چون برابربا تکس باکس درصورتی که داخا ردف اول هم محمد رو داریم

danialafshari
شنبه 12 خرداد 1397, 10:18 صبح
ببنید داخل دیتا بیس فیلد Lname شامل (محمد,رضا,مهدی) داخل یک ردیف ردیف دوم شامل (حسین,حسن)ردیف سوم (محمد) و الی اخر
خوب حلا ما یک تکس منوسیم محمد وقتی اجرا مگریم فقط ردیف سوم میاره چون برابربا تکس باکس درصورتی که داخا ردف اول هم محمد رو داریم
خوب توی همون پست قبل همین رو میگفتی
باید ToList() کنی
من به شکل زیر تست کردم و مشکلی نداشت
مثال:

TestEntities database = new TestEntities();
var list = database.People.Where(p => p.FirstName.Contains(textBox1.Text.Trim())).ToList ();
dataGridView1.DataSource = list;

یا به شکل بالا

using (TestEntities context = new TestEntities())
{
var persons = context.People.AsQueryable();
persons = persons.Where(p => p.FirstName.Contains(textBox1.Text.Trim()));
dataGridView1.DataSource = persons.ToList();
}

موفق باشید

Helpco
شنبه 12 خرداد 1397, 11:47 صبح
خوب توی همون پست قبل همین رو میگفتی
باید ToList() کنی
من به شکل زیر تست کردم و مشکلی نداشت
مثال:

TestEntities database = new TestEntities();
var list = database.People.Where(p => p.FirstName.Contains(textBox1.Text.Trim())).ToList ();
dataGridView1.DataSource = list;

یا به شکل بالا

using (TestEntities context = new TestEntities())
{
var persons = context.People.AsQueryable();
persons = persons.Where(p => p.FirstName.Contains(textBox1.Text.Trim()));
dataGridView1.DataSource = persons.ToList();
}

موفق باشید

فکنم هنوز منظورم درست نرسوندم
ببنید من از این روش استفاده میکنم
tview.Contains(x.tview)

tview این لیست می باشد که جای همون تکس باکس بلا فقط بجای یکه مورد چند مورد داخلش می باشد و x.tview فیلده دیتابیس که شامل اسامی که بالا گفتم که با (,)از هم جداشده الان چطوری جستجو کنیم چون میاد داخل x.tview فقز اونهای که تک هست را با لیست tview برابر میکنه اگر دوتای باشه حساب نمیکنه
یعنی یک جوری دوتا لیت باهم مخوایم چک کنیم که لیست دوم ممکن ردیف هاش با (,) ازهم جدا شده باشند

Mahmoud.Afrad
شنبه 12 خرداد 1397, 22:02 عصر
فکنم هنوز منظورم درست نرسوندم
ببنید من از این روش استفاده میکنم
tview.Contains(x.tview)

tview این لیست می باشد که جای همون تکس باکس بلا فقط بجای یکه مورد چند مورد داخلش می باشد و x.tview فیلده دیتابیس که شامل اسامی که بالا گفتم که با (,)از هم جداشده الان چطوری جستجو کنیم چون میاد داخل x.tview فقز اونهای که تک هست را با لیست tview برابر میکنه اگر دوتای باشه حساب نمیکنه
یعنی یک جوری دوتا لیت باهم مخوایم چک کنیم که لیست دوم ممکن ردیف هاش با (,) ازهم جدا شده باشند

یعنی در تکست باکس هم چند کلمه با ویرگول جدا شدن ؟

Helpco
یک شنبه 13 خرداد 1397, 07:24 صبح
یعنی در تکست باکس هم چند کلمه با ویرگول جدا شدن ؟
بله دقیقا همین مورد هم هست

Mahmoud.Afrad
یک شنبه 13 خرداد 1397, 22:54 عصر
بله دقیقا همین مورد هم هست

اول آیتمهای درون تکست باکس را split کنید. در کوئری با متد any چک کنید فیلد مورد نظر حاوی هرکدام از آیتمها بود سطر را سلکت کنید.
string str = "item1,item2";
List<string> searchList = str.Split(',').ToList();

var result =
from row in context.TableName
where searchList.Any(item => row.FieldName.Contains(item))
select row;

booysusa
سه شنبه 12 فروردین 1399, 08:27 صبح
درود بر شما مهندس

بسیار عالی توضیح دادید و برنامه بسیار عالی نوشته شده است.
اگر امکانش هست همین سورس را بصورت کدفرست در بیارید و آپلود کنید
قطعا اگر این کار را بلد بودم در این تایپیک نمی گفتم، پس دوستان از سادگی این کار نگن، چون واقعا قبل اینکه این تایپیک را باز کنم، قبلش در اینترنت سرچ کردم، ولی چیزی دستگیرم نشد

سپاس

booysusa
سه شنبه 12 فروردین 1399, 10:08 صبح
درود دوباره
مهندس من سعی کردم کد بالا را به روش کدفرست بنویسم، مشکلی که الان باهاش بر خوردم این هست که من دیگه نمیتونم مخاطبی را که قبلا ذخیره کردم ویرایش کنم، منظورم این هست که فرم ویرایش مخاطب دیگه لود نمیشه

کدهارو میزارم بررسی کنید لطفا

کدهای بخش فرم Main
namespace PhoneBook.Contact
{
public partial class MainContact : Form
{
private readonly BindingSource m_BindingSource = new BindingSource();

public MainContact()
{
InitializeComponent();
}

Stopwatch swTime = new Stopwatch();

IEnumerable<Models.Contact> allRecords;

private void LoadContact()
{
//using (var db = new PhoneContext())
// this.m_BindingSource.DataSource = db.Contacts.ToList();
//this.dgvContact.DataSource = this.m_BindingSource;


//// لود به روش جستو جدید
using (PhoneContext context = new PhoneContext())
{
allRecords = context.Contacts.ToList();
dgvContact.DataSource = allRecords;

}
}


// فرم لود
private void MainContact_Load(object sender, EventArgs e)
{
LoadContact();
}


private Models.Contact GetDataGridCurrentRow()
{
return this.m_BindingSource.Current as Models.Contact;
}

// دکمه ویرایش مخاطب
private void btnEditShowContact_Click(object sender, EventArgs e)
{
var row = this.GetDataGridCurrentRow();
if (row == null) return;

using (var f = new frmNewContact())
{
f.DbLoad(row.Id);
f.ShowDialog(this);
}

this.Loadcontact();
}


وقتی اطلاعات رو اینجوری لود میکنم مشکلی پیش نمیاد، ولی خوب بدیش این هست که دیگه جستجو درست عمل نمی کنه و خطا میده
private void LoadContact()
{
using (var db = new PhoneContext())
this.m_BindingSource.DataSource = db.Contacts.ToList();
this.dgvContact.DataSource = this.m_BindingSource;

}



وقتی کد زیر رو درون فرم لود قرار میدم، دیگه دکمه ویرایش اطلاعات مخاطب عمل نمیکنه
IEnumerable<Models.Contact> allRecords;

private void LoadContact()
{
using (PhoneContext context = new PhoneContext())
{
allRecords = context.Contacts.ToList();
dgvContact.DataSource = allRecords;

}
}


چه کار کنم؟:اشتباه: