PDA

View Full Version : الگوریتم برای فقط آخرین فراخوانیِ متد



SajjadKhati
شنبه 06 خرداد 1402, 12:33 عصر
سلام دوستان

فرض کنید که کد زیر را داریم :



public class A
{
private void Caller_1()
{
Test();
}

public static void Test()
{
}
}


public class B
{
private void Caller_2()
{
A.Test();
}

private void Caller_3()
{
A.Test();
}
}


متدهای با نام Caller در کلاس های مختلف ، قرار هست که یک متد در یک کلاس (متد A.Test) را فراخوانی کنند .
میخوام آخرین باری که یک متد ، متد Test را فراخوانی کرد ، فقط همون بار این فراخوانی انجام بشه .

یعنی مثلا اگر متد Caller_1 ، اول اجرا شد ، متد Test را فراخوانی نکنه و اگر بعدش Caller_3 فراخوانی شد ، باز هم متد Test را فراخوانی نکنه و فقط آخرین دفعه ای که فرضا Caller_2 اجرا شد ، فقط این بار ، متد Test را فراخوانی کنه .

معلوم هم نیست که ترتیب اجرای متدهای Caller ، چطوری هست . مثلا ممکنه در یک دفعه ، فقط یکی از این متدها اجرا بشه و دفعه ی بعد ، 3 تاشون اجرا بشن و همچنین اینکه ممکنه ترتیب هاشون فرق کنن .

==============

من فقط راهکار استفاده از تایمر به ذهنم میرسه .
یعنی متدهای Caller ، مستقیما متد Test را فراخوانی نکنن و متدی در یه کلاس با یه تایمر را فراخوانی کنن و اون تایمر ، بعد از یک مدت زمان خاص ، فقط یک بار متد Test را فراخوانی کنه .

که البته این روش ، چندان بهینه نیست . چون ممکنه در یک سیستمی ، وقتی این یک یا دو یا سه متدهای Caller فراخوانی میشن ، مثلا بینِ دو فراخوانیِ این متدها ، مدت زمانش رسیده باشه و اون تایمر ، متد Test را فراخوانی کرده باشه (که در این صورت ، متد Test ، فقط آخرین بار ، اجرا نمیشه و چند بار اجرا میشه) و یا اینکه ممکنه که در یک سیستم دیگه ، مدت زمانش نرسیده باشه و در این صورت ، بیخودی هدر رفتِ زمان و معطلی را در اون سیستم ایجاد میکنه .

دوستان راهکار بهینه تری سراغ دارن؟
تشکر

پرستو پارسایی
شنبه 06 خرداد 1402, 20:41 عصر
با سلام . ضمن تشکر از زحمات سال های دور . لطفا این کد را در مورد سوال فوق تست بفرمائید.
public class A
{
private static DateTime? lastTestCall = null;
private static Dictionary<string, DateTime> lastCallerCalls = new Dictionary<string, DateTime>();

private void Caller_1()
{
if (!lastTestCall.HasValue && (!lastCallerCalls.ContainsKey("Caller_1") || lastCallerCalls["Caller_1"] < lastTestCall))
{
Test();
}
lastCallerCalls["Caller_1"] = DateTime.Now;
}

public static void Test()
{
lastTestCall = DateTime.Now;
// کد مربوط به متد Test
}
}


public class B
{
private void Caller_2()
{
if (!A.lastTestCall.HasValue && (!A.lastCallerCalls.ContainsKey("Caller_2") || A.lastCallerCalls["Caller_2"] < A.lastTestCall))
{
A.Test();
}
A.lastCallerCalls["Caller_2"] = DateTime.Now;
}

private void Caller_3()
{
if (!A.lastTestCall.HasValue && (!A.lastCallerCalls.ContainsKey("Caller_3") || A.lastCallerCalls["Caller_3"] < A.lastTestCall))
{
A.Test();
}
A.lastCallerCalls["Caller_3"] = DateTime.Now;

ShayanFiroozi
شنبه 06 خرداد 1402, 20:45 عصر
سلام ،

میتونین 3 تا متغیر استاتیک به عنوان Flag در نظر بگیرین که در زمان اجرای هر کدوم از Caller ها Flag همون Caller برابر با true بشه ، در هر Caller اگر 2 تا از Flag ها true بودن یعنی در این Caller باید Test اجرا بشه ، و وقتی اجرا شد Flag هاتون رو دوباره false کنین.

SajjadKhati
یک شنبه 07 خرداد 1402, 00:10 صبح
با سلام . ضمن تشکر از زحمات سال های دور . لطفا این کد را در مورد سوال فوق تست بفرمائید.
public class A
{
private static DateTime? lastTestCall = null;
private static Dictionary<string, DateTime> lastCallerCalls = new Dictionary<string, DateTime>();

private void Caller_1()
{
if (!lastTestCall.HasValue && (!lastCallerCalls.ContainsKey("Caller_1") || lastCallerCalls["Caller_1"] < lastTestCall))
{
Test();
}
lastCallerCalls["Caller_1"] = DateTime.Now;
}

public static void Test()
{
lastTestCall = DateTime.Now;
// کد مربوط به متد Test
}
}


public class B
{
private void Caller_2()
{
if (!A.lastTestCall.HasValue && (!A.lastCallerCalls.ContainsKey("Caller_2") || A.lastCallerCalls["Caller_2"] < A.lastTestCall))
{
A.Test();
}
A.lastCallerCalls["Caller_2"] = DateTime.Now;
}

private void Caller_3()
{
if (!A.lastTestCall.HasValue && (!A.lastCallerCalls.ContainsKey("Caller_3") || A.lastCallerCalls["Caller_3"] < A.lastTestCall))
{
A.Test();
}
A.lastCallerCalls["Caller_3"] = DateTime.Now;


سلام ،

میتونین 3 تا متغیر استاتیک به عنوان Flag در نظر بگیرین که در زمان اجرای هر کدوم از Caller ها Flag همون Caller برابر با true بشه ، در هر Caller اگر 2 تا از Flag ها true بودن یعنی در این Caller باید Test اجرا بشه ، و وقتی اجرا شد Flag هاتون رو دوباره false کنین.

سلام
خیلی ممنون از هر دوی شما .
خواهش میکنم .

در کدتون ، اون متدهای Caller ، بررسی میکنن اگر متغییر lastTestCall مقدار نداشت ، کد اجرا میشه که در این صورت فقط یکبار کد اجرا میشه .
همچنین اینکه روال کار هر دوی شما بزرگواران چون شبیه همدیگه هست ، توضیحات زیر را بدم :

فکر کنم درک مسئله ای که مطرح کردم ، خوب انجام نشد .
من میخوام مثلا Caller_1 ، وقتی صدا زده شد ، آیا متد Test فراخوانی بشه؟
هنوز خودِ متد Caller_1 نمیدونه . باید بفهمه که آیا متدهای Caller_2 یا Caller_3 هم قرار هست که در آینده ، متدِ Test را فراخوانی کنند یا نه .
اگر یک کدوم از اینها میخوان فراخوانی کنند ، خوب متد Caller_1 ، نباید متد Test را فراخوانی کنه وگرنه باید فراخوانی کنه .

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

ShayanFiroozi
یک شنبه 07 خرداد 1402, 07:11 صبح
هنوز خودِ متد Caller_1 نمیدونه . باید بفهمه که آیا متدهای Caller_2 یا Caller_3 هم قرار هست که در آینده ، متدِ Test را فراخوانی کنند یا نه .


بسیار خوب ، شرایط اینکه میخوان اجرا کنند یا نه چی هست ؟

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

مرسی:لبخندساده:

پرستو پارسایی
یک شنبه 07 خرداد 1402, 13:59 عصر
من هم درک مناسبی از مطالب ذکر شده نداشتم با اینحال چیزی که متوجه شدم اینه :

// Class A with Test method and callerspublic class A
{
// Singleton instance of class A
private static A instance = null;
// Last time Test method was called
private DateTime? lastTestCall = null;
// Dictionary to store last call time of each caller
private Dictionary<string, DateTime> lastCallerCalls = new Dictionary<string, DateTime>();
// Get the singleton instance of class A
public static A Instance
{
get
{
if (instance == null)
{
instance = new A();
}
return instance;
}
}


// Private constructor for singleton pattern
private A() { }


// Check if Test method can be called based on last call times of Test and callers
private bool CanCallTest()
{
return !lastTestCall.HasValue || lastCallerCalls.Values.All(callTime => callTime < lastTestCall.Value);
}


// Update the last call time of a caller
private void UpdateLastCallerCall(string callerName)
{
lastCallerCalls[callerName] = DateTime.Now;
}


// Caller_1 method that calls Test method if conditions are met
public void Caller_1()
{
if (CanCallTest())
{
Test();
}
UpdateLastCallerCall("Caller_1");
}


// Test method that performs a certain task
private void Test()
{
lastTestCall = DateTime.Now;
// Code for the Test method
NotifyObservers();
}


// List of observers that need to be updated when Test method is called
private List<IObserver> observers = new List<IObserver>();


// Attach an observer to the list
public void Attach(IObserver observer)
{
observers.Add(observer);
}


// Detach an observer from the list
public void Detach(IObserver observer)
{
observers.Remove(observer);
}


// Notify all observers in the list
private void NotifyObservers()
{
foreach (IObserver observer in observers)
{
observer.Update();
}
}
}

وبرای کلاس B

// Class B with callers that call Test method in class Apublic class B
{
// Instance of class A
private A a = A.Instance;


// Caller_2 method that calls Test method in class A if conditions are met
public void Caller_2()
{
if (a.CanCallTest())
{
a.Test();
}
a.UpdateLastCallerCall("Caller_2");
}


// Caller_3 method that calls Test method in class A if conditions are met
public void Caller_3()
{
if (a.CanCallTest())
{
a.Test();
}
a.UpdateLastCallerCall("Caller_3");
}
}

SajjadKhati
یک شنبه 07 خرداد 1402, 16:47 عصر
بسیار خوب ، شرایط اینکه میخوان اجرا کنند یا نه چی هست ؟


سلامی مجدد
ممنون .

دقیقا متوجه ی منظورتون نشدم .



یا اینکه شما بفرمایید هدف یا در واقع مسئله کلا چی هست تا شاید درک بهتری صورت بگیره.

مرسی:لبخندساده:


جریان اینه که کلاس ها و پروپرتی هایی هستند که Binding ئه 2 طرفه میخوام انجام بدم (در wpf) .
هدف بایندینگ (Binding Target) ، کنترل DataGrid هست و منبع بایندینگ (Binding Source) ، شی ای از کلاس Person (پروپرتی های مختلف در DataGrid ، به پروپرتی های مختلف در Person ، متصل و Binding ئه 2 طرفه میشن) .

کلاس Person ، علاوه بر پروپرتی هایی که اطلاعات نوع اساسی (مثل string و int و ...) را ذخیره میکنه ، پروپرتی هایی از نوع کالکشن ای از کلاس های دیگه مثل کلاس (MobileNumber و Address) را هم داره که این کلاس ها هم خودشون شامل پروپرتی ها از نوع داده های اساسی دارند که اطلاعات مورد نظر را ذخیره میکنند (کلاس Address در کد زیر نیامد) :



public class PhoneBook
{
public ObservableCollection<Person> Persons { get; set; }
}


public class Person : INotifyPropertyChanged
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public ObservableCollection<MobileNumber> MobileNumbers { get; set; }
}


public class MobileNumber : INotifyPropertyChanged
{
public int Id{ get; set; }
public string MobileNumber{ get; set; }
}


با انجام Binding ئه 2 طرفه ، نهایتا مثلا چند تا پروپرتی را که تغییر دادیم ، بخشِ (accessor) ئه set ئه پروپرتیِ مورد نظر تغییر میکنه .
مثلا کاربر ممکنه که FirstName و MobileNumber را در پروپرتی های بالا ، تغییر بده یا اضافه کنه (ممکنه حتی یه دونه پروپرتی یا ممکنه خیلی بیشتر از این ها را تغییر بده یا اضافه کنه) .

اما من فقط شیِ Person را میخوام . یعنی چه یه پروپرتی تغییر کنه یا مقدار جدیدی اضافه بشه یا اینکه 10 تا پروپرتی تغییر کنن ، واسه ی من ، فقط مهم این هست که این پروپرتی ها فقط یکبار اعلام کنن که شیِ Person ام تغییر کردن (اون هم آخرین پروپرتی ای که تغییر کرد ، باید اعلام کنه) .

یعنی مثلا کاربر که اول ، FirstName را تغییر داد و بعد MobileNumber را تغییر داد ، واسه ی من ، فقط تغییر مقدار پروپرتی آخری که MobileNumber هست ، مهم هست .

-------------------

از طرفی هم در Binding ئه دو طرفه ، چون مدیریت اضافه و کم کردن کدها ، چندان دست ما نیست (کنترل ها و موتور بایندینگ wpf ، بصورت اتوماتیک این کار را میکنه) ، خیلی نمیشه زمان کدها را مدیریت کامل کرد . البته شدن ، شاید بشه که توضیح میدم .

یعنی مثلا مثل حالت عادی که یه فرم باز میشه و وقتی کاربر فرم را بست ، برنامه نویس از رویدادِ بستنِ فرم متوجه میشه که مقادیر توسط کاربر داده شد ، در این حالت ، چندان این طور نیست و باید از رویدادهایی که پراکنده هستن ، این موارد را متوجه شد .


هر چند ، برای من ، فقط حذف و اضافه و ویرایش شدن شیِ Person مهم هست (یعنی حذف و اضافه و ویرایش شدن شی زیر مجموعه اش که MobileNumber و ... باشه ، فعلا مهم نیست) و موقع حذف و اضافه و ویرایش شدن شیِ Person ، میتونیم توسط کلاسی که از ValidationRule ارث بری میکنه ، به نوعی از زمان حذف و اضافه و ویرایش شدن شیِ Person با خبر بشیم اما بخاطر مشکلی در طراحی کنترل DataGrid (اطلاعاتی که میخواد برای ویرایش شدن بفرسته را قبل از اینکه به لایه ی منطق تجاری بفرسته ، به ما نمیده ؛ باز هم هر چند میشه کدنویسی این مشکل را رفع کرد اما چون کدها را پیچیده میکنه و مخصوصا ممکنه در ویرایش نسخه های بعدی ، کار را سخت کنه ، از این روش استفاده نمیکنم) ، باید جوری این رو تنظیم کنم که بعد از اینکه اطلاعات را به لایه ی منطق تجاری منتقل کرد ، بعد متدی در کلاس ValidationRule اجرا بشه .

بنابراین راهکار دیگه ای فعلا پیدا نکردم که بتونم متوجه بشم که کاربر چه وقتی فقط شیِ Person را حذف یا اضافه و ویرایش میکنه (مگر اینکه از تغییر تک تکِ هر پروپرتی در هر کلاس متوجه بشم که این هم ، همین مشکلی را داره که در این تاپیک مطرح کردم) .

نمیدونم چرا کنترل اعتبارسنجی را در DataGrid ، این طوری طراحی کرد مایکروسافت!
نمیدونم کنترل های شرکت های دیگه هم همین مشکل را داره (که قبل از ارسال داده اش به Binding Source ، اون اطلاعات را به کاربر ارائه میده) یا نه !
تشکر

SajjadKhati
یک شنبه 07 خرداد 1402, 16:55 عصر
من هم درک مناسبی از مطالب ذکر شده نداشتم با اینحال چیزی که متوجه شدم اینه :

// Class A with Test method and callerspublic class A
{
// Singleton instance of class A
private static A instance = null;
// Last time Test method was called
private DateTime? lastTestCall = null;
// Dictionary to store last call time of each caller
private Dictionary<string, DateTime> lastCallerCalls = new Dictionary<string, DateTime>();
// Get the singleton instance of class A
public static A Instance
{
get
{
if (instance == null)
{
instance = new A();
}
return instance;
}
}


// Private constructor for singleton pattern
private A() { }


// Check if Test method can be called based on last call times of Test and callers
private bool CanCallTest()
{
return !lastTestCall.HasValue || lastCallerCalls.Values.All(callTime => callTime < lastTestCall.Value);
}


// Update the last call time of a caller
private void UpdateLastCallerCall(string callerName)
{
lastCallerCalls[callerName] = DateTime.Now;
}


// Caller_1 method that calls Test method if conditions are met
public void Caller_1()
{
if (CanCallTest())
{
Test();
}
UpdateLastCallerCall("Caller_1");
}


// Test method that performs a certain task
private void Test()
{
lastTestCall = DateTime.Now;
// Code for the Test method
NotifyObservers();
}


// List of observers that need to be updated when Test method is called
private List<IObserver> observers = new List<IObserver>();


// Attach an observer to the list
public void Attach(IObserver observer)
{
observers.Add(observer);
}


// Detach an observer from the list
public void Detach(IObserver observer)
{
observers.Remove(observer);
}


// Notify all observers in the list
private void NotifyObservers()
{
foreach (IObserver observer in observers)
{
observer.Update();
}
}
}

وبرای کلاس B

// Class B with callers that call Test method in class Apublic class B
{
// Instance of class A
private A a = A.Instance;


// Caller_2 method that calls Test method in class A if conditions are met
public void Caller_2()
{
if (a.CanCallTest())
{
a.Test();
}
a.UpdateLastCallerCall("Caller_2");
}


// Caller_3 method that calls Test method in class A if conditions are met
public void Caller_3()
{
if (a.CanCallTest())
{
a.Test();
}
a.UpdateLastCallerCall("Caller_3");
}
}

سلامی مجدد
ممنون .

به نظر میرسه روال کد ، همون مثل قبل هست .
اما سناریو را گفته بودم . ببینید سناریو اش ، خیلی شبیه به سناریوی مزایده هست .
مشتری هایی وجود دارند (متدهای Caller) که میخوان به فروشنده (متد Test) ، یک قیمتی را اعلام کنند (متدش را فراخوانی کنند) .
اما آیا فروشنده ، با اعلام هر مشتری ، لازم میدونه که عمل فروش را انجام بده؟
نه .
بلکه فروشنده ، با هر بار اعلامِ مشتری ، باز از همه ی مشتریانِ دیگه میپرسه که آیا قصد خریدِ کالا اش را دارند یا نه .
سناریو ، شبیه به این هست .

لطفا کد ندین (مگر اینکه متوجه نشم) . اگه مایل بودید ، اول ، سناریوی تون را برای حل این مشکل بگین ، ببینم شدنی هست؟
تشکر

ShayanFiroozi
یک شنبه 07 خرداد 1402, 17:07 عصر
اضافه ، حذف و یا ویرایش به صورت مستقیم روی Datagrid انجام میشه ؟

واینکه فکر کنم Observabe Pattern که خانم پارسایی توضیح دادن بتونه کمک کنه.
ضمنا Datagrid شرکت های دیگه رو تجربه نکردم ، باشد تست کنین.

و در خصوص راه حل خودتون



بنابراین راهکار دیگه ای فعلا پیدا نکردم که بتونم متوجه بشم که کاربر چه وقتی فقط شیِ Person را حذف یا اضافه و ویرایش میکنه (مگر اینکه از تغییر تک تکِ هر پروپرتی در هر کلاس متوجه بشم که این هم ، همین مشکلی را داره که در این تاپیک مطرح کردم) .
تشکر

اگر روش شما فقط مشکل بهینه نبودن رو داره که شاید بشه کاریش کرد ، اگر پیاده سازی کردین لطف کنین ارسال کنین ، شاید با استفاده از Reflection ها بتونیم روش جدید پیاده سازی کنیم.

SajjadKhati
یک شنبه 07 خرداد 1402, 19:32 عصر
اضافه ، حذف و یا ویرایش به صورت مستقیم روی Datagrid انجام میشه ؟


توسط Binding ئه 2 طرفه انجام میشه .
مستقیما کدی را برای تغییر نوشته نمیشه (مگر اینکه لایه ی منطق تجاری را از لایه ی view بخوام دستکاری کنم که اون هم شاید انجام نشه) (حداقل اینکه خیلی بعیده مخصوصا تغییر در view) .



واینکه فکر کنم Observabe Pattern که خانم پارسایی توضیح دادن بتونه کمک کنه.
ضمنا Datagrid شرکت های دیگه رو تجربه نکردم ، باشد تست کنین.


Observer Pattern ، برای اطلاع دادن یک وضعیت هست . مثلا مقداری از یک شی ای میخواهد تغییر کند یا اینکه تغییر کرد را اطلاع میدهد .

اما من میخوام بدونم آیا وقتی این اتفاق افتاد (مقدار یک شی تغییر کرد) ، آیا شی های دیگر هم مقدارشون در آینده تغییر میکنند یا نه؟
آینده ای که هنوز اتفاق نیفتاد ، و اون شی هنوز نمیدونه ، حداقل با این الگوی طراحی قابل پیاده سازی نیست .

ضمن اینکه اصلا معلوم نیست قابل پیاده سازی باشه یا نه .
مثلا شاید با الگوریتم های پیچیده ی هوش مصنوعی بشه متوجه شد اما من بلد نیستم .



و در خصوص راه حل خودتون



اگر روش شما فقط مشکل بهینه نبودن رو داره که شاید بشه کاریش کرد ، اگر پیاده سازی کردین لطف کنین ارسال کنین ، شاید با استفاده از Reflection ها بتونیم روش جدید پیاده سازی کنیم.


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

تشکر .

Mahmoud.Afrad
دوشنبه 08 خرداد 1402, 09:16 صبح
آقای SajjadKhati (https://barnamenevis.org/member.php?363659-SajjadKhati) چرا اینقدر یک مسئله رو بد توضیح میدی. تازه توی پست هفتم ما باید بفهمیم سوال اصلی چیه!!!


فکر کن داری برای ویندوزفرم یا هر پلتفرم دیگری کد مینویسی. چطور متوجه میشی که یک سطر ویرایش شده و آماده اعتبارسنجی و ثبت تغییرات در دیتابیس هست؟

پرستو پارسایی
دوشنبه 08 خرداد 1402, 10:23 صبح
سلام با توجه به نحوه پرسش آقای Mohamoud.Afrad گرامی ، به نظرم برای اینکه بتوانیم در ویندوز فرم به تغییرات در داده ها ی ویرایش شده دسترسی پیدا کرده و آن ها را اعتبارسنجی و در دیتابیس ذخیره کنیم، می توانیم از رویداد PropertyChanged استفاده کنیم و در آن، کدی برای اعتبارسنجی و ذخیره تغییرات در دیتابیس بنویسیم.


public class Person : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;


private int id;
public int Id
{
get { return id; }
set
{
if (id != value)
{
id = value;
OnPropertyChanged(nameof(Id));
}
}
}


private string firstName;
public string FirstName
{
get { return firstName; }
set
{
if (firstName != value)
{
firstName = value;
OnPropertyChanged(nameof(FirstName));
}
}
}


private string lastName;
public string LastName
{
get { return lastName; }
set
{
if (lastName != value)
{
lastName = value;
OnPropertyChanged(nameof(LastName));
}
}
}


public ObservableCollection<MobileNumber> MobileNumbers { get; set; }


protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}

در این کد، برای هر ویژگی که تغییر می کند، رویداد PropertyChanged فراخوانی می شود و با استفاده از nameof()، نام ویژگی تغییر یافته را به عنوان پارامتر به رویداد PropertyChanged ارسال می شود. سپس در بخش مدیریت رویداد می توانید کدی برای اعتبارسنجی و ذخیره تغییرات در دیتابیس بنویسیم

public void Person_PropertyChanged(object sender, PropertyChangedEventArgs e){
if (e.PropertyName == nameof(Person.FirstName))
{
// اعتبارسنجی و ذخیره تغییرات در دیتابیس
}
else if (e.PropertyName == nameof(Person.LastName))
{
// اعتبارسنجی و ذخیره تغییرات در دیتابیس
}

البته این کد بر مبنای کد ارسالی خودتان بود و برای اینکه منظورم را درست عنوان کنم کد ارسال کردم

SajjadKhati
دوشنبه 08 خرداد 1402, 17:36 عصر
آقای SajjadKhati (https://barnamenevis.org/member.php?363659-SajjadKhati) چرا اینقدر یک مسئله رو بد توضیح میدی. تازه توی پست هفتم ما باید بفهمیم سوال اصلی چیه!!!


سلام آقا محمد .
آقا شهروز گفتن که جریان کلی را توضیح بدم (شاید روال دیگه ای باشه و کلا این روال لازم نباشه) ، برای همین توی پست 7 توضیحات کلی را دادم .

بله درست میگید . ببخشید .
راستش مسائل ریز و این نوع طراحی باعث شده خودم هم یه کم گیج بشم و چند روال متفاوت را بررسی کنم .

خلاصه اینکه بجای این روش ، از ترکیبی از رویدادهای DataGrid.RowEditEnding و DataGrid.AddingNewItem و فلگ هایی در کلاس مشتق ValidationRule و همچنین تنظیم اجرای ValidationRule بعد از تنظیم و تغییر پروپرتی ها در Model استفاده کردم (در تست اولیه فعلا الحمدلله کار میکنه) .



فکر کن داری برای ویندوزفرم یا هر پلتفرم دیگری کد مینویسی. چطور متوجه میشی که یک سطر ویرایش شده و آماده اعتبارسنجی و ثبت تغییرات در دیتابیس هست؟


پلتفرم شاید زیاد مهم نباشه (برای اطلاعات بیشتر ، گفتم) .
مسئله ، Binding ئه دو طرفه بود که توضیح داده بودم . یعنی مثلا جوری نیست که یک فرم ای بسته بشه و بعد از بستن ، کنترل کامل در دست برنامه نویس باشه که چه کار کنه (که در پست قبلی توضیح بیشتر داده بودم) .

SajjadKhati
دوشنبه 08 خرداد 1402, 17:38 عصر
سلام با توجه به نحوه پرسش آقای Mohamoud.Afrad گرامی ، به نظرم برای اینکه بتوانیم در ویندوز فرم به تغییرات در داده ها ی ویرایش شده دسترسی پیدا کرده و آن ها را اعتبارسنجی و در دیتابیس ذخیره کنیم، می توانیم از رویداد PropertyChanged استفاده کنیم و در آن، کدی برای اعتبارسنجی و ذخیره تغییرات در دیتابیس بنویسیم.


public class Person : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;


private int id;
public int Id
{
get { return id; }
set
{
if (id != value)
{
id = value;
OnPropertyChanged(nameof(Id));
}
}
}


private string firstName;
public string FirstName
{
get { return firstName; }
set
{
if (firstName != value)
{
firstName = value;
OnPropertyChanged(nameof(FirstName));
}
}
}


private string lastName;
public string LastName
{
get { return lastName; }
set
{
if (lastName != value)
{
lastName = value;
OnPropertyChanged(nameof(LastName));
}
}
}


public ObservableCollection<MobileNumber> MobileNumbers { get; set; }


protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}

در این کد، برای هر ویژگی که تغییر می کند، رویداد PropertyChanged فراخوانی می شود و با استفاده از nameof()، نام ویژگی تغییر یافته را به عنوان پارامتر به رویداد PropertyChanged ارسال می شود. سپس در بخش مدیریت رویداد می توانید کدی برای اعتبارسنجی و ذخیره تغییرات در دیتابیس بنویسیم

public void Person_PropertyChanged(object sender, PropertyChangedEventArgs e){
if (e.PropertyName == nameof(Person.FirstName))
{
// اعتبارسنجی و ذخیره تغییرات در دیتابیس
}
else if (e.PropertyName == nameof(Person.LastName))
{
// اعتبارسنجی و ذخیره تغییرات در دیتابیس
}

البته این کد بر مبنای کد ارسالی خودتان بود و برای اینکه منظورم را درست عنوان کنم کد ارسال کردم

سلام مجدد
خیلی ممنونم .
از یه روال دیگه رفتم و فعلا الحمدلله کار میکنه (که در پست قبلی توضیح دادم) .

تشکر

Mahmoud.Afrad
دوشنبه 08 خرداد 1402, 22:42 عصر
آفرین، توی این مساله پلتفرم مهم نیست پس اینکه بایندینگ دوطرفه دارید هم مهم نیست.

راه حل
1- بعد از اعمال تغییرات، کاربر را مجبور به ثبت تغییرات کنید. مثلا با فشردن یک دکمه.
2- با تغییر هر پراپرتی ، تغییرات رو اعمال کنید. (رویداد در سطح مدل) ... مثال خانم پارسایی
3- رویداد در سطح view . رویدادهایی مثل تغییر سطر یا خارج شدن از حالت ادیت یک سلول یا ردیف دیتاگرید، که خودتون ازش استفاده کردید