יום חמישי, 24 בפברואר 2011

Simple exntesnion for Task exception handling

כשעובדים עם Task ההמלצה של MSDN היא לתפוס את ה Exceptions ע"י Wait בתוך try-catch
מה שגורם לסרבול של הקוד , ובנוסף תוקע את ה task במקום לעבוד אסינכרוני.
כתבתי extension method שמאפשר לעשות logging ולטפל ב exception במקום אחד מרכזי.
ע"י שימוש ב HandleExceptionAsync
Task.Factory.StartNew(() =>
{
throw new Exception("test exception");
}).HandleExceptionAsync();


הדוגמא שלי גם רושמת ל logger שהוא חלק מהפרויקט שלי.

בנוסף ניתן להירשם ל event שיעלה בכל פעם שיש exception:
TaskHandlerExtensions.TaskExceptionRaised += t =>
{
   //process here the exception... 
   //t.Exception
};
Task.Factory.StartNew(() =>
{
throw new Exception("test exception");
}).HandleExceptionAsync();

את הקוד המלא ניתן להוריד מכאן (העליתי את כל הקוד כי אני עובד עם תשתית logging )  הקובץ הספציפי נמצא ב:

Diagnostics\Roniz.Diagnostics.Logging\TaskHandlerExtensions.cs

או ניתן להורדה מכאן רק את הקובץ הספציפי ולשנות אם יש צורך את ה dependency ל ILog

יום ראשון, 13 בפברואר 2011

Scalable state synchronization using P2P - part 3 – איך להשתמש בתשתית באפליקציה שלך

בפעם הקודמת הסברתי על היכולות של התשתית ואיך להריץ את האפליקציה לדוגמא , בפוסט הזה אני אסביר איך כל אחד יכול לעבוד איתה בפרוייקט שלו.

 

אם לא מעוניינים ב NuGet אפשר ישר לקפוץ לשלב הבא "הוספה ידנית – ללא NuGet"

התקנה באמצעות NuGet

אני מתבסס על זה שכבר יודעים מה זה NuGet , אם בכל זאת לא מכירים אפשר לקרוא כאן ובעברית

Add Library Package reference:
Add library package reference

חיפוש והתקנה של החבילה P2PSync
Add library package reference dialog
Add library package reference dialog - licence

לאחר שהחבילה הותקנה נוספים לפרויקט מספר assemblies וכמו כן קובץ app.config מתעדכן (או נוצר אם לא היה קודם)
P2PSync - package installed

ה assemblies:
(dependencies for NLog)
NLog.dll
NLog.Extended.dll – הקובץ הזה איננו חובה וניתן להוריד אותו , במיוחד באפליקציות עם .Net client profile – שאז הוא לא מתאים.
Roniz.Diagnostics.Logging.dll - מעטפת שלי ל NLog
כל ה assemblies הללו כדי לתמוך ב Logging , אני משתמש ב Log2Console , ניתן להוריד מ codeplex.
וה assemblies העיקריים של התשתית:
Roniz.WCF.P2P.Channels.dll
Roniz.WCF.P2P.Messages.dll
Roniz.WCF.P2P.Sync.dll
Roniz.Diagnostics.Logging.dll

הוספה ידנית – ללא NuGet

במידה ולא רוצים להשתמש ב NuGet צריך להוריד את הגרסה האחרונה מ codeplex.
ולהוסיף את הקבצים הבאים לפרויקט:

Roniz.Diagnostics.Logging.dll
Roniz.WCF.P2P.Channels.dll
Roniz.WCF.P2P.Messages.dll

בנוסף להוריד מ NLog ולהוסיף את הקבצים הבאים:
NLog.dll

מימוש ה business logic הספציפי של הפרויקט

המימוש נעשה ע"י ירושה מRoniz.WCF.P2P.Sync.Interfaces.ISynchronizationBusinessLogic - באפליקציה לדוגמא זה ה Roniz.WCF.P2P.ApplicationTester.MySynchronizationBusinessLogic

מימוש הודעות בין ה peers:

FullPresenceInfo מכיל את הנתונים שישלחו מה peer לשאר ה peers כשהסטטוס שלו נהפך ל Online , באפליקציה לדוגמא זה ה Roniz.WCF.P2P.ApplicationTester.Messages.MyFullPresenceInfo class.
CompactPresenceInfo - מכיל את הנתונים שישלחו מה peer לשאר ה peers כשקוראים ל close ב manager – זה המקום שה peers האחרים יכולים לדעת ש peer מסוים ירד ולפעול בהתאם , באפליקציה לדוגמא Roniz.WCF.P2P.ApplicationTester.Messages.MyCompactPresenceInfo.
BusinessLogicMessageBase – ה base class שמשמש את כל ההודעות ה business logics האחרות (למשל הודעות עדכונים).

שימוש בקוד (הקוד מהאפליקציה לדוגמא):
אתחול synchronization manager:
כשמאתחלים את SynchronizationStateManager צריך לספק לו את הclass שמממש את ה businesslogic:
SyncManager = new SynchronizationStateManager(MySynchronizationBusinessLogic);

וכשרוצים להתחיל לסנכרן:
SyncManager.Open();

שליחת נתונים לאחר הסנכרון הראשוני
במהלך הריצה של האפליקציה בדרך כלל יש צורך בשליחת עדכונים לשאר ה הpeers , ניתן לעשות זאת באמצעות Update

לדוגמא:
var updateState = new MyStateContainer
{
  StateDictionary = new Dictionary<Guid, MyUserUpdateState>(1)
};
updateState.StateDictionary.Add(id, UserState);
SyncManager.Update(updateState);

הפסקת סנכרון – סגירת ה manager:
הודעה לשאר ה peer שה peer הנוכחי יורד
SyncManager.Close();

יום חמישי, 10 בפברואר 2011

ObservableDictionary of Dr WPF with dispatcher support

אני משתמש הרבה פעמים ב ObservableDictionary של DR WPF

היתה לי בעיה שחוזרת על עצמה כאשר מוסיפים או מורידים איברים מה dictionary ב thread אחר , הוא זורק את ה CollectionChanged / PropertyChanged על אותו thread שבוא נעשה השינוי וזה מה שגורם ל exception:

"InvalidOperationException: The calling thread cannot access this object because a different thread owns it."

כדי לתת תמיכה מתוך ה ObservableDictionary עשיתי לו שינוי קטן שתומך בזריקת ה events על ה dispatcher הנכון:

1. הוספת dispatcher ו DispatcherPriority ל ctor's – כפרמטרים אופציונאליים


public ObservableDictionary(Dispatcher dispatcher = null,DispatcherPriority dispatcherPriority = DispatcherPriority.DataBind)
{
    _dispatcher = dispatcher;
    _dispatcherPriority = dispatcherPriority;
    _keyedEntryCollection = new KeyedDictionaryEntryCollection<TKey>();
}

2. במתודות OnCollectionChanged ו OnPropertyChanged אם יש dispatcher משתמשים בו כדי להעלות את ה  event על הUI thread הנכון.

protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs args)
{
    var handler = CollectionChanged;
    if (handler != null)
    {
    if (_dispatcher != null)
    {
        if (_dispatcher.CheckAccess())
        {
            handler(this, args);
        }
        else
        {
            _dispatcher.Invoke((Action)(() => handler(this, args)), _dispatcherPriority);
        }
    }
    else
    {
        handler(this, args);
    }
    }
}

ניתן להוריד את הקוד מכאן 

CCCheck has stopped working

פתאום משום מקום כשאני עושה build התחלתי לקבל את ההודעת שגיאה הזאת

image

הבעיה: CodeContracts אצלי עובד עם baseline

image

ולאחר שלקחתי את הפרוייקט מ source control כנראה הוא הפך את חלק מהקבצים ל read only מה שזרק את ה exception (ניתן לראות ב Output)

CodeContracts: Roniz.WCF.P2P.Messages: Unhandled Exception: System.UnauthorizedAccessException: Access to the path …\baseline.xml.new' is denied.

הורדת ה readonly מהקבצים פתרה את הבעיה

יום רביעי, 2 בפברואר 2011

Scalable state synchronization using P2P

בהרבה אפליקציות שמפתחים נדרש לשתף נתונים בין ה server ל client וגם בין מספר clients ל server אחד.

ככל שמספר ה clients גודל – כך העומס על השרת עולה , Azure פותר אולי את הבעיה ברמה הטכנית , אבל יש צורך לשלם על זה...

עם הזמן שמתי לב שיש דפוס כמעט קבוע שחוזר על עצמו בהרבה אפליקציות:

כשה client עולה הוא מסתנכרן מול ה server , וכך כל client נוסף שעולה חוזר על הפעולה.

כשיש שינוי באחד מה clients הוא מעדכן את ה server ומשם יש שתי אופציות:

1. או שה clients עושים pooling ל server כדי לקבל עדכונים.

2. או שה server שולח הודעה (למשל ב WCF ע"י מימוש Pub/Sub) לשאר ה clients.

לכאורה הפתרון השני הוא המועדף – אבל יש איתו מספר בעיות:

1. אם ה clients נמצאים מאחורי NAT או Firewall שמונע מה server שליחת הודעות ב callback אליהם , חייבים להחזיק socket פתוח אל ה server , ואז ככל שכמות ה clients עולה – כך כמות ה sockets הפתוחים בצד ה server.

2. בכמויות גדולות של clients ה server צריך לשלוח את אותה הודעה לכולם מה שיוצר אצלו ג"כ צוואר בקבוק מבחינת ביצועים.

אני מציג כאן תשתית שכתבתי המשתמשת ב PeerChannel (טכנולוגית P2P של מייקרוסופט מבוססת WCF) , כדי לבצע את אותו מנגנון סינכרון , כתבתי גם אפליקציה לטסטים ולדוגמא כיצד לעבוד עם התשתית.

חלק מהרעיונות למדתי מ Kevin Hoffman שפירסם מאמר ב msdn magazine

בעיות קיימות (מקווה לפתור בגרסה נוספת):

1. זמן פתיחה ארוך של ה proxy , וזמן נוסף שעובר לאחר שנוסף peer ל mesh עד ל online event ולתחילת הסנכרון.

2. בסיטואציות מסויימות יכול להיות mesh אחד שמכיל מספר peers אבל מספר קבוצות של peers כך שכל peer חושב שהוא Online ומסונכרן עם כל ה mesh , אבל הסנכרון שלו הוא רק עם התת קבוצה שאליה הוא מחובר , הפיתרון כרגע מבוסס על refresh שנעשה ב interval שניתן לשלוט בו (default 30 שניות בכל peer)

הקוד עדיין בתחילת הדרך , אשמח לקבל הערות ושיפורים.

ניתן להוריד אותו מכאן.

הסבר על האפליקציה לדוגמא:

כל חלון מייצג peer אחד ב mesh , ויש לו name ו data שניתן לערוך:

clip_image001

א. לאחר שלוחצים על connect ה peer פותח channel וה communication status שלו עובר ל opening ולאחר מכן opened, ברגע שה peer עובר לסטטוס opened הוא מכניס ל grid את הפרטים שלו , ועדיין לא מסונכרן מול peerים אחרים.

בדוגמא peer A ו peer B עברו ל Opened והכניסו את המידע על עצמם – עדיין ללא סנכרון , peer C עדיין לא נפתח ונמצא בסטטוס created

clip_image002

ב. אם הוא מוצא peer נוסף הוא עובר לסטטוס online , ומתחיל תהליך סנכרון מול ה peerים האחרים – שולח להם בקשה לסנכרון , מקבל את המידע ומציג אותו ב grid , אותו דבר קורה ב peerים האחרים שעברו למצב online

בדוגמא A ו B במצב Online ומסונכרנים ביניהם

clip_image003

ג. לחיצה על send update of own details תעדכן את כל ה peers האחרים בנתונים שיש באיזור ה user details.

בדוגמא D מעדכן את A B ו C ב "some new data":

clip_image004

ד. לחיצה על disconnect תשלח הודעה ל peerים האחרים על יציאה – והם ינקו את השורה שמייצגת את ה peer שירד.

clip_image005