درود,
جداولی با ارتباط زیر رو دارم ,
Tbl_Color : جدول رنگ ها
Tbl_ColorProduct : اختصاص دادن رنگ به یک محصول
در بخش جستجوی سایت میخوام وقتی کاربر چندتا رنگ رو انتخاب میکنه محصولاتی که همه اون رنگ های انتخابی در جدول Tbl_ColorProduct براشون ثبت شده رو نشون بدم.
- توضیح مراحل انجام کار :
بعد از اینکه کاربر در Ui سایت چندتا رنگ رو انتخاب کنه و تیک بزنه:
یک لیست از آی دی رنگ های انتخابی به سمت اکشن زیر فرستاده میشه :
و بعد روی جداول مربوط کوری زیر رو میزنم :
حالا این کوری رو چطور بنویسم که فقط محصولاتی رو انتخاب کنه که همه اون رنگ های انتخابی براشون در جدول Tbl_ColorProduct ثبت شده باشه ؟
کد زیر را بررسی کنید:
var colors = new List<int>(); Products = (from a in db.Tbl_Products from b in db.Tbl_ColorProduct where a.Id == b.Product_Id && colors.Contains(b.Color_Id) select a).ToList();
ممنون مهندس , تست کردم ,
تو این حالت اگه مثلا من رنگ آبی و زرد رو انتخاب کنم هم محصولی که رنگ آبی برایش تو جدول (Tbl_ColorProduct
) ثبت شده و هم محصولی که رنگ زرد براش ثبت شده رو نشون میده ,
من میخوام فقط محصولاتی سرچ بشن که جفش این دو رنگ انتخابی براشون تو جدول (Tbl_ColorProduct
) ثبت شده باشه (یعنی فقط اگه دوتا رنگ انخابی براشون ثبت شده بود تو نتیجه کوری باشن) .
var colors = new List<int>(); Products = (from a in db.Tbl_Products from b in db.Tbl_ColorProduct where a.Id == b.Product_Id && colors.Where(i => i == b.Color_Id) select a).ToList();
به جای Contains از Where استفاده کنید.
دستور where رو به اینصورت بنویسم که اخطار میده :
عذرخواهی میکنم اصلاح شد:
colors.Where(i => i == b.Color_Id).Any()
یا
colors.Any(i => i == b.Color_Id)
خواهش میکنم , نتیجه این کوری هم دقیقا مثل Contains میشه و اگه دوتا رنگ رو انتخاب کنم هر محصولی که یکی از رنگ ها براش ثبت شده باشه تو نتیجه کوری میاد .
عذرمیخوام مهندس متوجه توضیحی که بالا دادم شدید ؟!
من میخوام انتخاب محدودتر باشه و فقط محصولی انتخاب بشه که همه رنگ های انتخابی براش ثبت شده باشه ؟
مدل های Product و productColor را قرار بدید.
.........................................................................................................
مدل هایی که توسط EF تولید شده و در برنامه شما وجود دارد را نمایش دهید.
..........................................................................................
بنده میخوای کدهای سی شارپ رو بررسی کنم Ef یه سری مدل به صورت کلاس برروی پروژه شما قرار داده است. برروی sloution خود نام جداول را جستجو کنید تا بتوان آن را پیدا کنید.
شرمنده مهندس, منظورتون همینه ؟
Tbl_Color
using System; using System.Collections.Generic; public partial class Tbl_Color { [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")] public Tbl_Color() { this.Tbl_ColorProduct = new HashSet<Tbl_ColorProduct>(); } public int Id { get; set; } public string Title { get; set; } public string CodeOrName { get; set; } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] public virtual ICollection<Tbl_ColorProduct> Tbl_ColorProduct { get; set; } }
Tbl_ColorProduct{
using System; using System.Collections.Generic; public partial class Tbl_ColorProduct { public int Id { get; set; } public int Color_Id { get; set; } public int Product_Id { get; set; } public virtual Tbl_Color Tbl_Color { get; set; } public virtual Tbl_Products Tbl_Products { get; set; } }
این کد هم بررسی کنید:
var colorFilter = new List<int>(); var colors = db.Colors.Where(c => colorFilter.Any(x => x == c.ColorId)).ToList(); var products = db.Products.Where(p => p.ProductColor.Any(pc => colors.Any(x => x == pc.ColorId))).ToList();
اگر بازهم نشد به جای Any() از All() استفاده کنید.
var colorFilter = new List<int>(); var colors = db.Colors.Where(c => colorFilter.Any(x => x == c.ColorId)).ToList(); var products = db.Products.Where(p => p.ProductColor.Any(pc => colors.Any(x => x.Id == pc.ColorId))).ToList();
در خط دوم از کدی که گذاشتید c.ColorId رو نمیشه گذاشت چون کوری روی خود جدول Tbl_Color هست و Id رو نوشتم .
در خط بعد هم اخطار رو میبینید .
کد های اصلاح شده جدید را تست کردید (بنده intellisense ندارم به همین دلیل اشتباه نوشتم اون قسمت رو)
شرمنده مهندس که وقتت رو گرفتم ,
به این شکل نوشتم ولی خط آخر رو که میخونه به خطا میخوره و به قسمت Catch میره .
var colorFilter = new List<int>(PColor); var colors = db.Tbl_Color.Where(c => colorFilter.Any(x => x == c.Id)).ToList(); var products = db.Tbl_Products.Where(p => p.Tbl_ColorProduct.Any(pc => colors.Any(x => x.Id == pc.Color_Id))).ToList();
راهی هست که خطای رخ داده رو ببینیم ؟
مهندس همون کد قبلی که گفتید :
var colors = new List<int>(PColor); Products = (from a in db.Tbl_Products from b in db.Tbl_ColorProduct where a.Id == b.Product_Id && colors.Where(i => i == b.Color_Id).Any() select a).ToList();
بخوام به جای From دوم از جوین استفاده کنم کد رو به چه صورت باید ویرایش کنم , میشه لطف کنید این رو هم بگید که تست کنم ؟
شما از Lazy Load استفاده می کنید به همین دلیل نیاز نیست join بزنید رابطه ایجاد شده به صورت خودکار واکشی می شود.
برای نمایش خطا هم از کد زیر استفاده کنید:
try { // code } catch (Exception e) { Console.WriteLine(e.Message); throw; }
اگه کد رو بصورت زیر بنویسم :
IEnumerable<Tbl_Products> Products = Bl_Product.Select(); // انتخاب همه محصولات var colors = new List<int>(PColors); foreach (var color in colors) { Products = (from a in Products from b in db.Tbl_ColorProduct where a.Id == b.Product_Id && b.Color_Id == color select a).ToList(); }
همون خروجی مدنظرم رو میده ولی خوب به تعداد رنگ هایی که انتخاب میشه حلقه و کوری تکرار میشه که جالب نیست .
باید کوئری تولید شده را بررسی کنیم و چند راه کار را امتحان کنیم:
var query = db.tBl_Product.AsNoTracking(); var colors = new List<int>(PColors); foreach (var color in colors) { query = query.Where(x => x.Tbl_ColorProduct.Any(o => o.Color_Id == color)).AsQueryable() } var result = query.ToList(); db.Database.Log = log => Debug.WriteLine(log);
برای مشاهده کوئری پنجره output را باز کنید.
بازهم ممنون ,
بصورت زیر نوشتم بازهم هر محصولی که یکی از رنگ هی انتخابی کاربرا رو داشت برگشت میده :
بصورت زیر نوشتم اوکی شد :
کاربرد اون .AsNoTracking(); چیه ؟
و اون Log که خط آخر نوشتید ؟
حالا به این صورت که گفتید بنویسم دیگه حله ؟ نوشتن کوری داخل حلقه که باعث ایرادی بعدا نمیشه ؟
در آخر گفتم کوئری که توسط این کد تولید شده را با استفاده از کدی که نوشتیم میتوانیم مشاهده کنیم. حال برای این کار پنجره ای در VS وجود دارد به نام output برای مشاهده آن کوئری میتوان آن پنجره را باز کنید.(برای این کار از EF Profiler هم می توانید استفاده کنید.)
AsNoTracking را در مقاله ای که برای بهبودی عملکر نوشته بودم توضیح دادم .
رفتم دوباره مقاله رو نگاه کردم , ریپوزیتوری Select رو بصورت AsNoTracking کردم .
برای بخش سایز و رنگ بنظرم همین کدی که گفتید عالیه ,
میخوام بین چک فیلتر های یک فیلتر خاص کوری بصورت Or اجرا بشه و بین دوتا فیلتر مختلف رابطه And باشه .
نتیجه این کوری همون چیزی هست که میخوام ,
مشکل اینه که با اجرا شدن چند باره ی کوری بخش Or یک محصول چندبار انتخاب میشه و در نتیجه میبینید که یک محصول چند بار نمایش داده میشه .
- برای جلوگیری از ذخیره سطر های تکراری در لیست IEnumerable<Tbl_Products> Products چه کدی رو باید بنویسم ؟
- یا میشه در آخر یک دستور روی Products بنویسم که سطر های تکراری رو حذف کنه ؟
هیچ کاربری تا کنون از این پست تشکر نکرده است
با ما تماس بگیرید تا در این مسیر همراهتان باشیم :)