PDA

View Full Version : حرفه ای: استاتیک بودن کلاس های BLL



wolf_majid
یک شنبه 12 بهمن 1393, 19:29 عصر
سلام دوستان
کلاس زیر یکی از کلاس های لایه بیزینس منه

public static class ActivityRepository
{
static ActivityRepository()
{
Entities = new MYDBEntities();
}

private static MYDBEntities Entities { set; get; }

private enum FieldForSearch
{
None
};

/// <summary>
/// جایگزین null
/// </summary>
/// <returns></returns>
private static IQueryable<Activity> GetNull()
{
var result = SelectAll().Take(0);
return result;
}

private static IQueryable<Activity> Select(FieldForSearch field, Activity activity = null)
{
if (activity == null) return field == FieldForSearch.None ? Entities.Activity : GetNull();
switch (field)
{
case FieldForSearch.None:
return Entities.Activity;

default:
throw new ArgumentOutOfRangeException("field");
}
}

/// <summary>
/// همه رکوردها را بر میگرداند
/// </summary>
/// <returns></returns>
public static IQueryable<Activity> SelectAll()
{
return Select(FieldForSearch.None);
}

/// <summary>
/// یک رکورد بر اساس کلید بر میگرداند
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public static Activity SelectFromId(int id)
{
return id < 1 ? null : Entities.Activity.SingleOrDefault(x => x.Id == id);
}


public static bool Add(Activity activity)
{
var exceptions = new StringBuilder();
if (activity == null)
throw new Exception("پارامتر ورودی نباید نال باشد");
if (SelectFromId(activity.Id) != null)
exceptions.AppendLine("یک فعایت با این کد در بانک اطلاعاتی وجود دارد");
if (string.IsNullOrEmpty(activity.Name))
exceptions.AppendLine("نام فعالیت باید دارای مقدار باشد");
if (exceptions.Length > 0)
throw new Exception(exceptions.ToString());


Entities.Activity.Add(activity);
Entities.SaveChanges();
return true;
}

public static bool Edit(Activity activity)
{
var exceptions = new StringBuilder();
if (activity == null)
throw new Exception("پارامتر ورودی نباید نال باشد");
var fetchById = SelectFromId(activity.Id);
if (fetchById == null)
exceptions.AppendLine("فعالیتی با این کد در بانک اطلاعاتی موجود نیست");
if (string.IsNullOrEmpty(activity.Name))
exceptions.AppendLine("نام فعالیت باید دارای مقدار باشد");
if (exceptions.Length > 0)
throw new Exception(exceptions.ToString());

if (fetchById != null)
{
var fetchByIdType = fetchById.GetType();
foreach (var property in activity.GetType().GetProperties())
{
var propertyInfo = fetchByIdType.GetProperty(property.Name);
var value = property.GetValue(activity, null);
propertyInfo.SetValue(fetchById, value, null);
}
}

Entities.SaveChanges();
return true;
}

}

چند تا سوال داشتم
1- آیا استاتیک بودن نمونه entity باعث بوجود آمدن احتمال تغییرات ناخواسته نمیشه وقتی به متد SaveChange میرسه (ممکنه بعضی از رفرنس ها تغییر پیدا کنند بدون اینکه متدهای کلاس صدا زده بشند)
2- آیا روشی که در پیش گرفتم روش درستی در چند لایه ای بودن به کمک entity هست
3- آیا استفاده از بایندینگ سورس در کنار این روش کار درستیه ؟
4- در کل اگر مشکلی داره یا بهتر از این هم میشه عمل کرد ممنون میشم راهنماییم کنید

خواهشا" جواب بدید

ali_md110
یک شنبه 12 بهمن 1393, 20:59 عصر
سلام
معمولا توی برنامه های چند لایه کلاسهای استاتیک برای یک سری متد عمومی کاربرد داره که در تمام پروژ هاتون صدا زده میشه
کلاسها در رو لایه های مختلف بصورت Abstract یا با استفاده از رابط ها برای ارث بری استفاده میکنن و لایه ها رو به هم رفرنس میدن
روش شما فقط یک کلاس استاتیک هست
و مشخص نیست چند لایه تو برنامتون دارید و به چه صورت به هم رفرنس داده شده اند
در ضمن بکارگیری متدی مثل GetAll که یک IQueryable بر میگردونه یک دوباره کاری و یک متد نشتی دار هست
سعی کنید از IQueryable درون یک متدی که از نوع List یا Observable باشه استفاده کنید .
اگر منطورتون از روش بایندینگ استفاده در wpf هست که بیشتر باید با ObservableCollection استفاده کنید

SabaSabouhi
دوشنبه 13 بهمن 1393, 09:51 صبح
سلام
خیلی خیلی جدی توصیه می‌کنم این entities استاتیک رو حذف کنی.
هر جایی که خواستی کار کنی با یه using یک entities ایجاد کن و ازش استفاده کن.
تو متدهای کمکی هم این شی رو به عنوان پارامتر بفرست.

حداقل دو مورد مشکل اساسی که این روش برات ایجاد می‌کنه یکی هنگام استفاده از transaction هست و
دیگه هنگامی که دو تا کلاس Bl بخوان با هم در ارتباط باشن.

من معمولاً این کار رو می‌کنم که متدهای public از کلاسم entities رو خودشون ایجاد می‌کنن و متدهای private یا internal
اون زو به عنوان یکی از پارامترها دریافت می‌کنن.
و اگر بیش از یک عمل منجر به ذخیره شدن تو متد وجود داشته باشه حتماً از transaction استفاده کن.

صبا صبوحی

wolf_majid
دوشنبه 13 بهمن 1393, 10:27 صبح
سلام
ممنون از جواباتون


...
روش شما فقط یک کلاس استاتیک هست
و مشخص نیست چند لایه تو برنامتون دارید و به چه صورت به هم رفرنس داده شده اند

توی برنامه 3 لایه وجود دارد
1- Dal که بوسیله ado.net entity data model ایجاد شده و تمامی عملیات مربوط به امنیت لایه مربوطه و ...
2- Bll که وظیفه عملیات بر روی Dal رو داره و تعریف پیش فرض ها , اعتبار سنجی ها و ...
3- UIL
مثالی از نحوه فراخوانی در پروژه تست :
[TestMethod]
public void ActivityCanEdit()
{
var s = Dal.SqlRepository.ActivityRepository.SelectAll().F irst();
var v = new Activity { Name = "تست مهر", Id = s.Id };
try
{
Dal.SqlRepository.ActivityRepository.Edit(v);
Assert.AreEqual("تست مهر", s.Name);
}
catch
{
Assert.Fail();
}
}




در ضمن بکارگیری متدی مثل GetAll که یک IQueryable بر میگردونه یک دوباره کاری و یک متد نشتی دار هست

این ساده ترین کلاسم در BLL ه , بعضی از کلاس های query های خیلی پیچیده داند و از اونجایی که نمیشه چقدر این کوئری ها نیاز به فیلتر دارند خروجی ها رو Iqueryable گذاشتم گه UIL بتونه بنا به نیاز داده های خاص خودش رو از بانک واکشی کنه و چندین بار فیلتر نشن


سعی کنید از IQueryable درون یک متدی که از نوع List یا Observable باشه استفاده کنید .

منظورتون رو نگرفتم

اگر منطورتون از روش بایندینگ استفاده در wpf هست که بیشتر باید با ObservableCollection استفاده کنید
نه منظورم binding source های win application بود , این قسمت رو بعد از تست کردن در یک پست دیگه میپرسم

wolf_majid
دوشنبه 13 بهمن 1393, 10:32 صبح
سلام
خیلی خیلی جدی توصیه می‌کنم این entities استاتیک رو حذف کنی.
هر جایی که خواستی کار کنی با یه using یک entities ایجاد کن و ازش استفاده کن.
تو متدهای کمکی هم این شی رو به عنوان پارامتر بفرست.
صبا صبوحی

ممنون
من هم همینکار رو کردم اما به یک مشکل اساسی بر خوردم
متد Edit مثال پست اولم رو ببینید
مقدار نمونه FetchbyId رو تغییر داده و بعد میخواد تغییرات رو ذخیره کنه اما دیگه نمونه Entity ایجاد شده در متد SelectFromId نابود شده و ساخت نمونه جدید هم رفرنس های جدید ایجاد میکنه که اصلا" نمیدونه fetchById چیه



حداقل دو مورد مشکل اساسی که این روش برات ایجاد می‌کنه یکی هنگام استفاده از transaction هست و
دیگه هنگامی که دو تا کلاس Bl بخوان با هم در ارتباط باشن.

من معمولاً این کار رو می‌کنم که متدهای public از کلاسم entities رو خودشون ایجاد می‌کنن و متدهای private یا internal
اون زو به عنوان یکی از پارامترها دریافت می‌کنن.
و اگر بیش از یک عمل منجر به ذخیره شدن تو متد وجود داشته باشه حتماً از transaction استفاده کن.


ممنون همین کار رو میکنم
اما اگر ممکنه کمکم کنید مشکل edit رو حل کنم

SabaSabouhi
دوشنبه 13 بهمن 1393, 14:41 عصر
ممنون
من هم همینکار رو کردم اما به یک مشکل اساسی بر خوردم
متد Edit مثال پست اولم رو ببینید
مقدار نمونه FetchbyId رو تغییر داده و بعد میخواد تغییرات رو ذخیره کنه اما دیگه نمونه Entity ایجاد شده در متد SelectFromId نابود شده و ساخت نمونه جدید هم رفرنس های جدید ایجاد میکنه که اصلا" نمیدونه fetchById چیه

اما اگر ممکنه کمکم کنید مشکل edit رو حل کنم

سلام
دوست عزیز، من اوایل کار با EF از این مشکلات زیاد داشتم، و تو پست قبلی هم راه حل کلی رو بهت گفتم. الان یه کم
بازترش می‌کنم.
در انجام یک کار ( مثلاً از فشرده شدن یک دکمه تا نمایش اطلاعات ) ممکن است تعداد زیادی query یا nonQuery داشته باشی.
از تعداد زیادی جدول بخونی، و تعداد جدول رو هم به‌روز کنی.
اگر می‌خوای بتونی با EF بدون مشکل کار کنی، «حتماً» فقط از یک شی Entities استفاده کن. تو متد اصلی BL که معمولاً از
لایه‌ی UI فراخوانی می‌شه یه entities باز کن ( من سعی می‌کنم همیشه این رو توی using انجام بدم )
و بقیه متدها entity رو از پارامتر دریافت کنن.
اون متد SelectFromId باید Entities رو به صورت پارامتر دریافت کنi. اگر این متد قرار هست public باشه
و از خارج از لایه‌ی BL هم دسترسی بهش باشه، باید دو نسخه ازش تولید کنی.


private myEntityObject SelectFromId( MyDbEntities entities, int id ){
return entities.MyTable.SingleOrDefault( x=>x.Id = id );
}
public myEntityObject SelectFromId( int id ){
using ( var entities = new MyDbEntities() ){
return SelectFromId( entities, id );
}
}


public bool Edit( myEntityObject myObject){
using ( var scope = new TransactionScope() )
using ( var entities = new MyDbEntities() ){
try{
... // Do you edit
entities.SaveChanges();
scope.Complete();
} catch{
}
}
}



هنگامی که از خارج از BL فراخوانی انجام بشه، ObjectContext جدید ساخته می‌شه ولی وقتی که
متد Edit شما که خودش ObjectContext رو ایجاد کرده بخواد این متد رو فراخوانی کنه متد اول رو
استفاده می‌کنه و به این شکل تداخل تو ObjectContextها پیش نمیاد.
به این صورت به راحتی و بدون مشکل هم می‌تونی از Transaction برای ذخیره‌سازی‌ها استفاده کنی.

صبا صبوحی

wolf_majid
دوشنبه 13 بهمن 1393, 19:30 عصر
ممنون
حل شد
اما حالا مجبورم خروجی متدهام رو IEnumerable بگذارم

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Transactions;

namespace Dal.SqlRepository
{
public static class ActivityRepository
{
private enum FieldForSearch
{
None
};

/// <summary>
/// جایگزین null
/// </summary>
/// <returns></returns>
private static IEnumerable<Activity> GetNull()
{
using (var entity = new MYDBEntities())
{
var result = entity.Activity.Take(0);
return result;
}
}

private static IEnumerable<Activity> Select(FieldForSearch field, MYDBEntities entities, Activity activity = null)
{
using (entities)
{
if (activity == null)
return field == FieldForSearch.None ? entities.Activity.ToList() : GetNull();
switch (field)
{
case FieldForSearch.None:
return entities.Activity.ToList();

default:
throw new ArgumentOutOfRangeException("field");
}
}
}


/// <summary>
/// همه رکوردها را بر میگرداند
/// </summary>
/// <returns></returns>
public static IEnumerable<Activity> SelectAll()
{
using (var entities = new MYDBEntities())
{
return Select(FieldForSearch.None, entities);
}
}

/// <summary>
/// یک رکورد بر اساس کلید بر میگرداند
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public static Activity SelectFromId(int id)
{
return id < 1 ? null : SelectAll().SingleOrDefault(x => x.Id == id);
}


public static bool Add(Activity activity)
{
using (var scope = new TransactionScope())
using (var entities = new MYDBEntities())
{
var exceptions = new StringBuilder();
if (activity == null)
throw new Exception("پارامتر ورودی نباید نال باشد");
if (SelectFromId(activity.Id) != null)
exceptions.AppendLine("یک فعایت با این کد در بانک اطلاعاتی وجود دارد");
if (string.IsNullOrEmpty(activity.Name))
exceptions.AppendLine("نام فعالیت باید دارای مقدار باشد");
if (exceptions.Length > 0)
throw new Exception(exceptions.ToString());


entities.Activity.Add(activity);
entities.SaveChanges();
scope.Complete();
return true;
}
}

public static bool Edit(Activity activity)
{
using (var scope = new TransactionScope())
using (var entities = new MYDBEntities())
{
var exceptions = new StringBuilder();
if (activity == null)
throw new Exception("پارامتر ورودی نباید نال باشد");
var fetchById = entities.Activity.SingleOrDefault(x => x.Id == activity.Id);
if (fetchById == null)
exceptions.AppendLine("فعالیتی با این کد در بانک اطلاعاتی موجود نیست");
if (string.IsNullOrEmpty(activity.Name))
exceptions.AppendLine("نام فعالیت باید دارای مقدار باشد");
if (exceptions.Length > 0)
throw new Exception(exceptions.ToString());

if (fetchById != null)
{
var fetchByIdType = fetchById.GetType();
foreach (var property in activity.GetType().GetProperties())
{
var propertyInfo = fetchByIdType.GetProperty(property.Name);
var value = property.GetValue(activity, null);
propertyInfo.SetValue(fetchById, value, null);
}
}
entities.SaveChanges();
scope.Complete();
return true;
}
}

}
}

Amirhossein_Bayat
دوشنبه 13 بهمن 1393, 20:54 عصر
سلام دوستان برای من سوالی پیش اومده لطفا کمک کنید.


static class Static
{
public static void meth()
{
Console.WriteLine("Run Static class");
}
}


class nonStatic
{
public static void meth()
{
Console.WriteLine("Run NonStatic class");
}
}

سوال این جاست که وقتی اسم هر کدوم ازکلاس ها رو میزنی بعد . میزنی اسم متد میاره. تو اولین کلاس که از نوع استاتیک میاره، اخه تو دومی برای چی میاره اون که استاتیک نیست فقط متد استاتیک.
مگه فقط دسترسی توی کلاس های استاتیک نیست؟ پس چرا تو کلاس دومی هم دسترسی مستقیم داریم؟

SabaSabouhi
سه شنبه 14 بهمن 1393, 10:15 صبح
سلام
این که یک کلاس static باشه یا نباشه ربطی به نوع فراخوانی متدهای static نداره.
بعضی از فرق‌های این دو نوع کلاس اینا هستن:
* کلاس استاتیک نمی‌تونه از کلاس دیگه‌ای ارث ببره
* کلاس استاتیک نمی‌تونه Interface رو Implement کنه
* از کلاس استاتیک نمی‌شه نمونه گرفت ( Insantiate )
* تو کلاس استاتیک نمی‌شه متد غیر استاتیک تعریف کرد

صبا صبوحی

wolf_majid
پنج شنبه 23 بهمن 1393, 16:40 عصر
سلام دوستان
دو کد زیر رو ببیند :

public static MYDBEntities Entities = new MYDBEntities();

public static IEnumerable<Activity> GetAll()
{
var x = Entities.Activity.ToList();
return x;
}

public static IEnumerable<Activity> SelectAll()
{
using (var entities = new MYDBEntities())
{
return Select(FieldForSearch.None, entities);
}
}

چکار کنم که متد دوم هم مثل متد اول فیلدهایی که کلید خارجی هستند یا خود کلاس کلید خارجی کلاس دیگری است را مقداردهی کند و استثنا پرتاب نکند . مثال :

عکس اول مربوط به متد GetAll است و HaghighiCompany رو مقداردهی کرده است :
128403
عکس دوم مربوط به متد SelectAll است و HaghighiCompany پرتاب استثنا کرده است و دلیلش هم این است که نمونه MyDbEntities نابود شده است :
128404

SabaSabouhi
جمعه 24 بهمن 1393, 00:40 صبح
سلام
متاسفانه توضیح مفهوم نبود. اما می‌تونم بگم که روش اول اشتباهه و باید از روش دوم استفاده کنی.
اگه بتونی مشکلت رو شفاف‌تر توضیح بدی، به احتمال خیلی زیاد می‌تونم کمکت کنم.

صبا صبوحی

wolf_majid
جمعه 24 بهمن 1393, 12:08 عصر
ممنون خانم صبوحی از پیگیریتون
فهمیدم با eagerly loading میشه مشکل رو حل کرد
اما در آخر تصمیم گرفتم کلا" Lazy Loading رو غیر فعال کنم که پرتاب استثنا نکنه

elec60
یک شنبه 26 بهمن 1393, 23:36 عصر
Where(i => (i.ferstande == txtferestande.Text && i.girande == txtgirande.Text || i.maghsad == txtmaghsad.Text || i.bar == txtbar.Text
|| i.date <= en && i.date >= st ).ToList();

با آیپد کپی کردم درب و داغون شد!

همه or ها رو داخل پرانتز بزار و بعد شرط تاریخ رو باهاش and کن and داخل شرط هم && بزار نه and از نوع bit wise

elec60
یک شنبه 26 بهمن 1393, 23:40 عصر
من تو پست دیگه ای ارسال کردم از اینجا سر درآورد!!!!!