נניח שיש לכם טבלה, עם עמודה הכוללת מספרים שלמים (int).באפליקציה שלכם, שלצורך העניין, משתמשת נניח ב LINQ to SQL או ADO.NET Entity Framework, אתם רוצים לקבל את הממוצע של העמודה הזאת. אתם אומרים לעצמכם שזה לא אמור להיות מורכב בכלל. יש Extension Method לאגרגרציה של נתונים, אחת מהם היא Average.
אתם כותבים אפליקציה פשוטה שמריצה אותה ואמורה לעשות את הממוצע של המספרים 1,1,2. התוצאה המצופה היא אחד ושליש. אתם מקבלים אחד. למה?
ההסבר פשוט מאד: משפט ה SQL שנוצר כתוצאה משימוש ב Average משתמש בפונקציית T-SQL בשם AVG. מישהו במיקרוסופט החליט, שכשמפעילים AVG על עמודה שה type שלה הוא int, אז הערך המוחזר יהיה גם הוא int. החלטה מעניינת, בהתחשב בעובדה שזה אומר שבמספר די רחב של מקרים Average לא תחזיר את הממוצע – אבל אני לא מתיימר להבין את (חוסר) ההיגיון מאחורי זה.
לכן, אפילו אם הערך שאתם רוצים לאכסן בעמודה הוא מספר שלם בהכרח, אבל רוצים לקבל את הממוצע האמיתי – כדאי שתגדירו את העמודה ל real.
בהצלחה.
בפוסט הקודם הצגתי את LINQ2DQLDTOCreator, אפליקציה קטנה שמייצרת Data Transfer Objects על בסיס LINQ to SQL Data Classes. היום ביצעתי קצת שיפורים באפליקציה והרחבתי אותה כך שהיא תייצר DTO’s גם ממודל של ADO.NET Entity Framework.
האפליקציה הנוכחית כוללת את כל היכולות של האפליקציה הקודמת. כלומר, עדיין ניתן לייצר DTO’s על בסיס LINQ to SQL Data Classes. אבל, עכשיו האפליקציה גם תייצר DTO’s על בסיס entities, כלומר מחלקות עם EdmEntityTypeAttribute.
בנוסף, כאשר עובדים מול ADO.NET Entity Framework האפליקציה גם מתייחסת ל relationships בין entities שונים. כלומר, אם במודל מוגדר של entityיש קשר של יחיד ליחיד עם ישות אחרת, הדבר ייוצג בם ב DTO (כמובן, באמצעות קשר ל-DTO הרלוונטי). גם קשר של יחיד לרבים ייוצג. למשל, הנה ה DTO שנוצר עבור הישות Products מהמסד נתונים Northwind:
using System;
using System.Data.Linq;
using System.Linq;
using System.Collections.Generic;
/*
This Data Transfer Object created Automatically by DTOGenerator.
You can download DTOGenerator and get support in the project site: http://dtogenerator.codeplex.com
DTOGenerator developed by Shahar Gvirtz (http://weblogs.asp.net/shahar)
*/
namespace DTO
{
public class ProductDTO
{
public static ProductDTO GetDTOFromDALObject(ConsoleApplication3.Product src, bool GetChilds)
{
ProductDTO obj = new ProductDTO();
obj.Discontinued = src.Discontinued;
obj.ProductID = src.ProductID;
obj.ProductName = src.ProductName;
obj.QuantityPerUnit = src.QuantityPerUnit;
obj.ReorderLevel = src.ReorderLevel;
obj.UnitPrice = src.UnitPrice;
obj.UnitsInStock = src.UnitsInStock;
obj.UnitsOnOrder = src.UnitsOnOrder;
if(src.Category != null && GetChilds)
obj.Category = CategoryDTO.GetDTOFromDALObject(src.Category,false);
if(src.Order_Details != null && GetChilds) {
List<Order_DetailDTO> Order_DetailDTOlst = new List<Order_DetailDTO>();
src.Order_Details.ToList().ForEach(p=>Order_DetailDTOlst.Add(Order_DetailDTO.GetDTOFromDALObject(p,false)));
obj.Order_Details = Order_DetailDTOlst;
}
if(src.Supplier != null && GetChilds)
obj.Supplier = SupplierDTO.GetDTOFromDALObject(src.Supplier,false);
return obj;
}
public ConsoleApplication3.Product GetDALObject(bool IncludeChilds)
{
ConsoleApplication3.Product obj = new ConsoleApplication3.Product();
obj.Discontinued = Discontinued;
obj.ProductID = ProductID;
obj.ProductName = ProductName;
obj.QuantityPerUnit = QuantityPerUnit;
obj.ReorderLevel = ReorderLevel;
obj.UnitPrice = UnitPrice;
obj.UnitsInStock = UnitsInStock;
obj.UnitsOnOrder = UnitsOnOrder;
if(Category != null && IncludeChilds)
obj.Category = Category.GetDALObject(false);
if(Order_Details != null && IncludeChilds) {
System.Data.Objects.DataClasses.EntityCollection<ConsoleApplication3.Order_Detail> Order_Detailsgetdallst = new System.Data.Objects.DataClasses.EntityCollection<ConsoleApplication3.Order_Detail>();
Order_Details.ForEach(p=>Order_Detailsgetdallst.Add(p.GetDALObject(false)));
obj.Order_Details = Order_Detailsgetdallst; }
if(Supplier != null && IncludeChilds)
obj.Supplier = Supplier.GetDALObject(false);
return obj;
}
public Boolean Discontinued { get; set; }
public Int32 ProductID { get; set; }
public String ProductName { get; set; }
public String QuantityPerUnit { get; set; }
public Int16? ReorderLevel { get; set; }
public Decimal? UnitPrice { get; set; }
public Int16? UnitsInStock { get; set; }
public Int16? UnitsOnOrder { get; set; }
public DTO.CategoryDTO Category { get; set; }
public List<DTO.Order_DetailDTO> Order_Details { get; set; }
public DTO.SupplierDTO Supplier { get; set; }
}
}
בנוסף, קיימת האפשרות לא לטעון את האובייקטים שקיים קשר אליהם מה DTO, באמצעות העברת false למתודות העזר GetDTOFromDALObject ו- GetDALObject שקיימות בכל DTO.
כדי לאפשר מעקב קל יותר אחרי גרסאות, יצרתי פרוייקט ב codeplex שנקרא DTOGenerator.
ניתן להוריד את הגרסא הראשונית החדשה (שתומכת גם ב LINQ to SQL וגם ב ADO.NET Entity Framework) מכאן.
בהצלחה.
הקדמה
באחד הפוסטים הקודמים כתבתי על Data Transfer Objects והשימוש בהם לעבודה נכונה ונוחה עם LINQ to SQL ו- ADO.NET Entity Framework.
בין התגובות שקיבלתי בפורום “תכנות .NET” בתפוז שבתגובה לשאלה שעלתה בו פורסם הפוסט, עלה העניין שלמרות שמדובר בכתיבה יותר נוחה, היא דורשת למעשה עבודה כפולה בהתחלה, וגם ציינתי זאת בפוסט. כשמתחילים לכתוב, יש צורך לכתוב DTO נפרד לכל אחת מהישויות עם תחילת העבודה, וגם בד”כ לכתוב מתודות שמבצעות העברה בין הDTO לאובייקט שנמצא ב DAL (האובייקט שמופה ע”י הORM) ולהיפך – לכתוב מתודה שמחזירה את ה DTO בהתאם לאובייקט שנמצא ב DAL. לטעמי, לא מדובר בהרבה עבודה, אבל יש כאלה שזה מפריע להם.
למי שהקצת יותר כתיבת קוד זה מה שמפריע לו, כתבתי היום במסגרת העבודה בסטארטאפסידס Code Generator קטן, שיוצר על סמך DataClasses של LINQ to SQL אובייקטים של DTO מקבילים ותואמים.
LINQ2SQLDTOCreator – האפליקציה עצמה
האפליקציה שכתבתי, ששמה הלא-מקורי-בעליל מופיע בכותרת של הפיסקה (LINQ2SQLDTOCreator), מקבלת נתיב לאסמבלי כלשהו (קובץ DLL של האפליקציה, למשל) ונתיב של המקום אליו היא תפלוט את התוצר שלה. האפליקציה עוברת על כל המחלקות ב DLL ומתעלמת מכולן למעט ממחלקות שמסומנות באטריביוט: System.Data.Linq.Mapping.TableAttribute שמסמל אובייקט שמייצג למעשה מיפוי של טבלה מהדטאבייס.
בכל אחת מהמחלקות הללו עוברת האפליקציה על כל ה properties שמסומנים באטריביוט System.Data.Linq.Mapping.ColumnAttribute שמסמן מיפוי של ה property לעמודה כלשהי בדטאבייס.
בסופו של דבר, מייצרת האפליקציה עבור כל אובייקט בDAL שממופה לטבלה, מחלקה (.cs) שמכילה הגדרות של properties עבור כל אחד מה types (כאשר ה properties הם מאותו ה type שהוגדרו ב DAL – במידה שהם מוגדרים ב database כ Allow Null, אז הם יוגדרו גם ב DTO כ Nullable Types). בנוסף, במחלקה הזאת, כלולות שתי מתודות באופן אוטומטי: מתודה סטאטית שמקבלת מופע מה type של האובייקט שבDAL שממופה לטבלה המקורית ומחזירה DTO, ומתודה שמביאה עבור המופע של הDTO את האובייקט DAL התואם, עם הערכים המתאימים (זה אמנם לא חלק מההגדרה של DTO, אבל שילבתי את זה לשם הנוחות. כמובן, שזה דורש שאם ה DTO בפרוייקט נפרד, אז יהיה רפרנס לDAL, כדי שאפשר יהיה ליצור אובייקט של הDAL ולקבל אותו).
לדוגמא, הנה קובץ שנוצר על-ידי האפליקציה:
1: using System;
2: /*
3: ------------------------DTO OBjECT-----------------------------
4: --------------Generated By LINQ2SQLDTOCreator------------------
5: ----------------Developed by Shahar Gvirtz---------------------
6: ---------http://blogs.microsoft.co.il/blogs/shahar-------------
7: ---------------http://weblogs.asp.net/shahar-------------------
8: */
9:
10: namespace DTO
11: {
12: public class VideoCategoryDTO
13: {
14: public static VideoCategoryDTO GetDTOFromDALObject( DAL.VideoCategory src )
15: {
16: VideoCategoryDTO obj = new VideoCategoryDTO ();
17: obj.ID = src.ID;
18: obj.Name = src.Name;
19: obj.Sorting = src.Sorting;
20:
21: return obj;
22: }
23: public DAL.VideoCategory GetDALObject()
24: {
25: DAL.VideoCategory obj = new DAL.VideoCategory ();
26: obj.ID = ID;
27: obj.Name = Name;
28: obj.Sorting = Sorting;
29:
30:
31: return obj;
32: }
33:
34: public Int32 ID { get; set; }
35: public String Name { get; set; }
36: public Int32 Sorting { get; set; }
37:
38:
39: }
40: }
השימוש באפליקציה הזאת פשוט יחסית:
- הורידו את האפליקציה
- הקובץ שהורדתם כולל את הקוד המלא. בתוך תיקיית הפרוייקט, בנתיב \bin\release תמצאו את קבצי ההרצה הנדרשים. העתיקו את כולם לאיזושהי תיקייה עם נתיב קצר לשם הנוחות.
- האפליקציה היא Console Application. בהרצה, היא מקבלת שני פרמטרים מופרדים ברווח. הראשון, הוא הנתיב ל DLL (אם הוא מכיל רווחים, יש לעטוף אותו במרכאות). השני, הוא הנתיב שבו רוצים את הפלט של הקבצי קוד שנוצרו. למשל:
1: c:\myApp\LINQ2SQLDTOCreator.exe "c:\Dev\App\bin\debug\logic.dll" "c:\outputfromapp"
שימו לב שהפרמטרים מופרדים עם רווח יחיד.
- האפליקציה תרוץ, ובתיקייה שהכנסתם תמצאו קבצים כמספר האובייקטים שממופים לטבלאות אצלכם באסמבלי. כל קובץ מורכב מהשם המקורי בסיומת DTO. את הקבצים האלה עליכם לשלב בפרוייקט (copy paste) במקום שאתם רוצים.
זכרו שאם אתם שמים בפרוייקט נפרד, יש צורך שהוא יכיל רפרנס לפרוייקט שמכיל את ה LINQ to SQL Data Classes.
- בהמשך, כל פעם שאתם מבצעים שינוי, אתם יכולים להריץ את האפליקציה שוב כדי לקבל DTO’s מעודכנים.
מגבלות שימוש ידועות
- האפליקציה אינה תומכת בקשרים בין האובייקטים. אם קיים Relationship בדטאבייס, בתהליך יצירת ה DTO יווצרו properties מתאימים לID בטבלה האחרת, אבל לא יהיה קיים קשר בין האובייקטים השונים.
- האפליקציה מסוגלת לעבוד עם assemblies שה Target Framework שלהם הוא .NET 3.5. היא לא מסוגלת לעבוד עם אסמבליס שה target framework שלהם הוא 4. ההסבר לזה מופיע פה. מי שממש חשוב לו, פשוט צריך לפתוח את הפרוייקט, להגדיר לו target framework של 4 והכל יעבוד טוב. לטובת כאלה שהם לא פריקים של בטות של מיקרוסופט, אני בניתי אותו ב-3.5.
- האפליקציה לא משלבת אוטומטית את הקבצים בפרוייקט ולא דואגת לנושאי רפרנסים וכו’.
במידה שאתם מוצאים בעיות נוספות, אני אשמח לשמוע ולתקן.
החלק הטכני – מה קורה מאחורי הקלעים?
כפי שכבר כתבתי בחלק הקודם, בגדול, מדובר ב Reflection לא מסובך במיוחד. האפליקציה מוצאת בהתאם ל attributes את הנתונים הרלוונטיים מבחינתה (השימוש ב attribute הוא כדי להבחין בין סתם class ל class של ה LINQ to SQL Data Classes. כעיקרון, ה properties היחידים שקיימים ב classes שנוצרים על ידי ה LINQ to SQL הם properties שממופים לעמודות. אבל, בכל זאת אני בודק בהתאם ל attributes, למקרה שהשמשתמש בחר לעשות שינויים בקבצים הללו בעצמו.
באפליקציה קיים Resource שמכיל קובץ טקסט שמהווה למעשה את התבנית למחלקות שנוצרות באמצעות המחולל. בתבנית יש מספר markers (מתחילים בסולמית) שלמקומות האלה מוכנסים (באמצעות replace פשוט) הערכים שנווצרים ע”י האפליקציה על סמך ה reflection.
מבחינת השימוש ב reflection – רובו פשוט למדיץ. הנקודה היחידה שייתכן שפחות מוכרת הוא העבודה מול ה Nullable types. כעיקרון, מי שלא יודע, nullable types זה למעשה מופעים של Nullable<T>. על-מנת לבדוק האם ה type של פרופרטי מסויים הוא nullable או לא, אני בודק האם מדובר ב type גנרי, והאם הוא שווה ל Nullable:
1: if (prop.PropertyType.IsGenericType && prop.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
2:
הסיבה לבדיקה, דרך אגב, היא כי אם אני ישר מדפיס את ה type באמצעות ה property שנקרא name, במידה ומדובר ב type גנרי הוא יודפס בצורה חסרת משמעות – למשל, Nullable’ (עם גרש בסוף). על-מנת שהקוד כן יעבור קומפילציה, אני משנה בידת הצורך את הסינטקס לסינטקס סי-שארפי.
סיכום
האפליקציה שהוצגה היא אפליקציה פשוטה, המשתמשת ב Reflection, על-מנת לייצר מחלקות Data Transfer Objects על סמך LINQ to SQL Data Classes. האפליקציה יכולה להיות שימושית מאד למפתחים המשתמשים ב LINQ to SQL ובORM-ים אחרים.
ניתן להוריד את האפליקציה מכאן.
שחר.
אפשרות חמודה שקיימת ב SQL Manegment Studio 2008 היא להגדיר לאופציית ה” GEnerate Scripts” של הדטאבייס גם לייצא את הנתונים (כלומר, לייצר משפטי INSERT לטבלאות).
האפשרות הזאת לא קיימת בגרסא 2005 (יש Generate Scripts – אין Data – כלומר שלמדתי על בשרי כשלא הבנתי לאן נעלמה האפשרות), אבל אין שום מגבלה על התקנה ושימוש ב Management Studio 2008 גם מול instance של SQL Server 2005.
השימוש העיקרי של האפשרות הזאת, הוא העברת מידע משרתי הפיתוח לפרודקשן כאשר רוצים להעביר טבלאות חדשות מסויימות או אובייקטים חדשים מסויימים בקלות. אפשר גם להעביר גטאבייס שלם בהתחלה, במידה שאין אפשרות לבקש ממנהל השרת (נניח, שרת שיתופי) לעשות restore מאיזשהו קובץ bak שאתם שולחים אליו. שימוש אפשרי נוסף הוא לגבות דטאבייס.
אז, איך עושים את זה?
- קליק ימני על הדטאבייס שרוצים לגבות, בחירה ב Tasks->Generate Scripts
- בשלב הבא בוחרים את הדטאבייס שממנו רוצים לגבות. אפשר לסמן כבר בשלב זה את ה checkbox שאומר Script all objects in the selected database. במידה שלא מסמנים אותו, יהיה צריך בהמשך לבחור מה מייצאים מה DB.
- מסך הבא הוא מסך האפשרויות. הרלוונטיות בינהן לרוב המשתמשים הן: Script Database Create (האם לייצר גם שאילתת Create ל-DB. טוב להפעלה הראשונה, אבל אם אתם מייצאים פריטים בודדים – לא תרצו את זה), Script for Server Version – מה גרסאת היעד שלה אמורים הסקריפטים להתאים, Script USE DATABASE – האם הסקריפט יכלול משפט Use ל DB מסויים (עלול להפריע אם שם ה DB בשרת היעד לא תואם לשם ה DB שממנו אתם מייצאים), Script DATA – האפשרות העיקרית שבגללה אני כותב את הפוסט הזה - האם לכלול משפטי INSERT לנתונים.
- בשלב הבא, אם סימנתם לייצר סקריפטים עבור כל האובייקטים ב DB, תעברו ישר לבחירה לאן לייצא את הסקריפט. אחרת, תידרשו קודם לבחור מה האובייקטים ב DB עבורם יווצרו סקריפטים.
- השלב האחרון הוא סיכום, שבו אפשר לראות את האופציות שנבחרו.
בהצלחה.
בעקבות שאלה שקיבלתי, כמה מילים על FooterTemplate ב Repeater: ה FooterTemplate מאפשר לקבוע תבנית עיצובית שתופיע בתחתית ה Repeater לאחר כל ה DataBound Items.
ניתן, כמובן, כמו כל מקום, לשלב שם כל Control שאתם רוצים. וכעת, לשאלה – איך ניגשים אליהם בלי להשתמש באף אחד מה events של ה Repeater? קצת מגושם, לא מורכב. בדוגמא הזאת, נניח של Repeater קוראים rpSample, ל control שאחנו מחפשים קוראים calDate והוא מהסוג Calendar. ככה הקוד ייראה:
Calendar myCal = (Calendar)rpSample.Controls[rpSample.Controls.Count - 1].Controls[0].FindControl("calDate")
בהצלחה.
כשעובדים מול LINQ to SQL או ADO.NET Entities Framework, אנחנו עובדים הרבה מאד מול אובייקט ה DataContext / Entities.
דוגמא מאד נפוצה בדוגמאות ובמדריכים של הטכנולוגיות הללו היא ליצור מופע של המודל בכל מתודה וכל פעם שמשתמשים בו. למשל:
1: public static void Method1()
2: {
3: using (var data = new DataClassesDataContext())
4: {
5: //Code
6: }
7: }
8: public static void Method2()
9: {
10: using (var data = new DataClassesDataContext())
11: {
12: //Code
13: }
14: }
וכך יש הרבה מתודות, כל אחת עושה new DataClassesDataContext. ואז מגיעה היום שבו רוצים לעשות איזשהו שינוי מינורי. רוצים לעשות connection string שונה בהתאם לסביבה או בהתאם לאיזשהו תנאי. איך עושים את זה? רמז – במקרה הזה, זה כנראה יהיה (במקרה הטוב) find&replace והרבה עבודת ידיים (במקרה הרע). וזה עוד לפני שדיברנו על דברים יותר מורכבים.
הפיתרון, כדי לחסוך עבודה במצבים כאלה ולהיות יותר גמישים לשינוי, הוא לכתוב איזושהי FactoryMethod. מתודה שמחזירה בעצמה מופע של DataClassesDataContext עם כל ההגדרות הנחוצות. אפשר כמובן לכתוב אותה בצורה גנרית, שתאפשר לנו גם לשנות בעתיד את סוג האובייקט שמוחזר, או לעבור בקלות ל ORM אחר, כל עוד הוא ממש איזשהו אינטרפייס – אבל זה במרבית המקרים יהיה קצת overkill. מה שכן יכול להתגלות כמאד משתלם, זה למקם במקום אחר מתודה כזאת:
1: public static DataClassesDataContext GetDataContextInstance()
2: {
3: return new DataClassesDataContext();
4: }
ואז לשנות את המתודות בהתאם, שבמקום לעשות new DataClassesDataContext הן יעבדו עם המתודה הזאת. בצורה הזאת, נניח אם מחר צריך לעשות Connection String שונה בהתאם לצורך, או אפילו שינויים יותר משמעותיים – הכל מרוכז במקום אחד.
בהצלחה.
אם רוצים ליצור צלמית של תמונה (גרסא מוקטנת שלה), ניתן כמובן לחתוך אותה באופן ידני. אולם, למי שלא מכיר (והרבה, כנראה, לא מכירים) הפונקציונאליות הזאת כבר מובנת ב BCL במחלקה System.Drawing.Image.
השימוש במתודה הזאת, GetThumbnailImage מאד פשוט. הנה לדוגמא קוד של Console Application שיוצר גרסא מוקטנת לכל התמונות בפורמט jpg שבתיקייה:
נעבור שנייה על הקוד הזה (שכפי שאתם רואים, הוא להדגמה בלבד):
1: ...
2: using System.Drawing;
3: using System.IO;
4: ...
5:
6: string dir = @"C:\Users\Public\Pictures\Sample Pictures";
7: int height = 60;
8: int width = 60;
9: string[] Images = Directory.GetFiles(dir, "*.jpg");
10: foreach (var image in Images)
11: {
12: Image.FromFile(image)
13: .GetThumbnailImage(width, height, delegate() { return false; }, IntPtr.Zero)
14: .Save(string.Format(@"c:\tmp\{0}",Path.GetFileNameWithoutExtension(image) + "_thumb.jpg"));
15: }
בשורות 6-8 אני מגדיר hard-coded את המידע שאני רוצה (dir היא תיקיית התמונות המקורית, ו height ו width מעידים על עצמם).
בשורה 9, אני מקבל את כל הקבצים שהסיומת שלהם jpg ונמצאים בתיקייה שציינתי.
לאחר מכן, אני עובד על כל הקבצים כאשר, בשורות 12-14 אני מבצע את הדברים הבאים: עבור כל נתיב, אני מקבל System.Drawing.Image. ובאמצעות המתודה GetThumbnailImage (שמקבלת רוחב, גובה, delegate ואת הערך של IntPtr.Zero שחייב להיות ככה) אני מקבל אובייקט Image נוסף, המכיל את התמונה המוקטנת, שאותו אני שומר לדיסק.
החיסרון בשיטה הזאת, הוא שאני לא שומר על יחס התמונה. במידה והיה מדובר במקרה אמיתי, צריך לוודא שהרוחב והגובה שנקבעים תואמים את יחס התמונה המקורי, אחרת נקבל תמונה לא פרופורציונאלית.
בהצלחה.
----------------------------
תודה, תודה, תודה לשלומי בן שמואל שהמליץ לי על התוסף הזה לכתיבת קוד. סוף סוף משהו שבאמת עובד!
כאשר מוסיפים שורה לטבלה המכילה איזושהי עמודה בשם ID שמוגדרת, לצורך העניין, כ Primary Key, פעמים רבות נרצה לקבל מיד אחרי הוספת הרשומה את ה ID שהוקצה לה.
באמצעות T-SQL, זה היה נעשה עם SCOPE_IDENTIFY(), בצורה הזאת:
1: SELECT SCOPE_IDENTIFY() AS [NewID]
זה היה מגיע לאחר ה INSERT, היינו מקבלים את הID וכולם היו שמחים ומאושרים. היום ראיתי דוגמאת קוד שבה מיד לאחר הוספת השורה ב LINQ to SQL וביצוע SubmitChanges, הולכים לבטלה ושולפים את השורה האחרונה בה כדי לקבל את הID. בסופו של דבר, התהליך הזה מיותר.
ברגע שמשתמשים במתודה InsertOnSubmit ומעבירים לה אובייקט שלו מוגדר, למשל, Primary Key מסוג int שמוגדר כ IsIdentify = True, לאחר ביצוע SubmitChanges ערך הID החדש יוכנס לאובייקט. למשל:
1: using (var data = new DataClassesDataContext())
2: {
3: Event ev = new Event(); //כאשר מדובר באובייקט שממופה לטבלה בדטאבייס
4: ev.Prop1 = "value1";
5: ev.Prop2 = "value2"
6: ....
7: data.Events.InsertOnSubmit(ev);
8: data.SubmitChanges();
9: Console.WriteLine(ev.ID); //ID is PK
10: }
אז, כאשר ID לא הוגדר ידנית, אבל הוא מוגדר באמת כ Primary Key, אז בעת ההוספה ל DB, מאחורי הקלעים באמת יישלף ה SCOPE_IDENIFY ויושם אוטומטית ל property של הPrimary Key באובייקט שהוספנו. נוח ושימושי.
בהצלחה.
נ.ב. חייב בדחיפות המלצה על דרך נורמלית לכתוב קוד באמצעות Windows Live Writer עם תוסף שגם יידע ליישר את הקוד לשמאל ורצוי שגם יידע לצבוע אותו. והכי חשוב, שאני לא אצטרך להעתיק ידנית את הקוד… תודה מראש לעוזרים.
בפורום “תכנות .NET” בתפוז, עלה לאחרונה נושא השילוב בין ADO.NET Entity Framework ומודל השכבות. ובאמת, אם נסתכל על הדוגמאות הנפוצות באינטרנט, מאד קל לראות עמודים ששמכילים ב code behind שלהם שאילתות LINQ שמקומם ב DAL ופעולות לוגיות שונות שמקומם ב BLL. בסופו של דבר, יש משהו מאד מפתה בנוחות שעלול לגרום ליצירת קוד מבולגן.
אם תחשבו על אפליקציה הבנוייה עפ”י מודל השכבות, מאד ברור לנו מה אמור להיות ב DAL ומה אמור להיות ב BLL ומה אמור להיות בGUI, למשל. אבל, השאלה היא, כשהאפליקציה מתבססת על LINQ to SQL/ ADO.NET EF כשכבת ה DAL, היא איך אנחנו מעבירים את המידע בין השכבות. שיטה שמאד מאד מקובלת בכל מיני דוגמאות באינטרנט, היא להשתמש באובייקטים שמופו מה DB ע”י ה ORM כBO’s ולמעשה, אותם להעביר בין השכבות.
שיטה זו בעייתית. דבר ראשון, האובייקטים שנוצרים באמצעות המיפוי של הORM-ים האלה עמוסים בהרבה מאד… זבל (לפחות, עד הגרסא הבאה של EF שתתמול בעבודה עם POCO). כלומר, דברים מאד מאד חשובים, אבל לא דברים שחשובים לאובייקטים ב BO שלנו. הם גם יורשים וממשים כל מיני דברים שיכולים “לבלגן” את האפליקציה שלנו (כל מתודות ה OnInsert וכו’).
הבעייתיות השנייה, היא ששימוש באובייקטים האלה כ-ORM כופה עלינו תלות משמעותית מדי ב ORM. אנחנו מסתמכים על האובייקטים שהוא מייצר בצורה שתגרום לכך שאם מתישהו נצטרך להחליף, התיקונים שלנו יהיו הרבה מעבר לתיקונים בDAL, אלא נצטרך לתקן גם ב BLL וגם ב GUI. או לחלופין, במידה שנחליף, נצטרך לעשות שמיניות באוויר כדי לשמור על איזושהי תאימות.
וחוץ מזה, נוצרת איזושהי פגיעה בהפרדה בין השכבות כשלGUI יש רפרנס ישיר ל DAL. המצב האידיאלי מבחינתי, הוא שלGUI יש רפרנס ל BLL שמחזיק בתורו רפרנס ל DAL ושלכולם יש רפרנס לBO’s.
אז, מה עושים? מה צורת העבודה המומלצת?
התשובה לזה קצת מורכבת, כי יש הרבה שיטות. השיטה שלי היא להשתמש ב DTO’s שגם משמשים כסוג של BO.
Data Transfer Objects הם אובייקטים שהייעוד העיקרי שלהם הוא העברת מידע בין השכבות. הם כמעט תמיד לא יכילו שום פונקציונאליות, למעט הצהרה על properties עם getters ו setters.
דוגמא מעשית:
כאשר משכבת ה GUI אני רוצה להוסיף, נניח, מאמר. לחיצה על כפתור ההוספה תגרור יצירת מופע של BO.ArticleDTO (אובייקט, שתפקידו להחזיק את המידע). המופע הזה יועבר כפרמטר למתודה שנמצאת, נניח, ב BLL.Articles.Insert. בBLL, למשל, אני אשלח אימייל על המאמר החדש שהתווסף לרשימת התפוצה, ואקרא ל DAL.Articles.Insert שאעביר אליו גם כן את אותו המופע של BO.ArticleDTO. בשכבת ה DAL, אני אצור מופע של DAL.Article (שהוא האובייקט שיצר ה ORM ושאיתו הוא עובד, כי הוא הרי לא מכיר את הDTO שלי) ואבצע את ההוספה.
במקרה של שליפה, מה ששכבת ה DAL תחזיר הלאה, זה גם כן מופע של BO.ArticleDTO.
חוץ מהעובדה ששימוש בשיטה הזאת פותר, למעשה, את הבעיות שציינתי כסיבה לא להשתמש באובייקטים שיוצר ה ORM, הוא גם נותן כמה יתרונות נוספים: דבר ראשון, ה DTO לא צריך לחפוך בשום צורה שהיא למודל ב DB, אין שום חובה שכל הנתונים שבו יהיו מקושרים ל DB, ולמעשה יש חופש מוחלט לגבי המבנה שלו.
מעבר לכך, במידה שקורה איזשהו שינוי בשכבת ה DAL, אין שום צורך לגעת באף שכבה עליונה יותר – כולם מכירים את ה DTO ועובדים מולו, והוא מאפשר ליצור איזושהי חציצה בין השכבות.
כמובן, שלשיטה הזאת יש חסרון – צריך, בפעם הראשונה, לעשות עבודה כפולה. ליצור את ה DTO. אבל, כבר לא פעם השיטה הזאת חסכה לי לא מעט עבודה בהמשך ואני ממליץ עליה בחום כשיטת עבודה כשעובדים עם ADO.NET EF או עם LINQ to SQL, לפחות עד ש EF יציג תמיכה סבירה בעבודה עם POCO.
בהצלחה.
במסגרת העבודה שלי בסטארטאפסידס, פיתחתי לאחרונה צ’אט אחד על אחד (סגנון gmail, פייסבוק וכו’) המאפשר לרשומים באתר לשוחח אחד עם השני.
מי שמכיר את הדוגמאות שנמצאות באינטרנט, בד”כ הנושא ממומש באמצעות שימוש כלשהו ב database, דבר שבאפליקציה שלנו היה בלתי אפשרי, ולכן החלטתי להשתמש במנגנון ה caching של ASP.NET. כתבתי באתר סטארטאפסידס מאמר קצר על מאחורי הקלעים של סידס צ’אט.
אתם מוזמנים לקרוא.
שחר.
אחת הפעולות הכי שימושיות שקיימות על מערכים, היא indexOf (אתה נותן את הערך של הפריט, וקמבל את האינדקס שלו במערך). כרום, פיירפוקס, ספארי - כל אלה תומכים בפעולה הזאת כמשהו מובנה,במנוע javascript שלהם על אף שהיא לא ב specification הרשמי. אינטרנט אקספלורר, בכל אופן, לא תומך בה.
אז מה עושים? יש פיתרון שמסתובב ברשת שלקוח למעשה מהמימוש של מוזילה עבור indexOf, שאפשר להוסיף אותו כ prototype function באקספלורר. הבעייה – מסתבר שזה גורם לעיתים בצורה לא מוסברת לאקספלורר להיתקע.
הפיתרון היותר נוח והיותר פשוט (בהנחה שאתם משתמשים ב jQuery) הוא לעבוד עם jQuery.inArray(a,b). כאשר הפרמטר הראשון (a) הוא ה value שאתם מחפשים והפרמטר השני (b) הוא המערך.
יחי jQuery.
כאשר עובדים עם profiler ניתן לייצא את הנתונים (וכך בד”כ עושים) לקובץ trc. במידה ורוצים לעבוד מול קבצי trc בקלות (בלי צורך בכל ה performance tools), ניתן לעבוד מולם באמצעות שאילתות TSQLפשוטות, בדיוק כמו טבלה. לצורך העניין, ניתן להשתמש ב fn_trace_gettable שמקבלת את שם הקובץ ומספר המסמן את כמות הקבצים שצריך לקרוא.
לצורך הדוגמא, השאילתה הבאה תחזיר לנו את השאילתה עצמה, שם האפליקציה שהריצה את השאילתה, זמן הפעולה שלה והCPU שצרכה ותמיין לפי הזמן שלקח להריץ כל שאילתה:
SELECT textdata,applicationname,duration,CPU
FROM ::fn_trace_gettable(('c:\startup.trc'),default)
WHERE duration is not null and textdata is not null
ORDER BY duration DESC
.csharpcode, .csharpcode pre
{
font-size: small;
text-direction: ltr;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
בהצלחה.
בהרבה אתרים, בייחוד אתרים קהילתיים, ניתנת לגולשים באתר אפשרות להעלות קבצים. בין אם זה attacments להודעות פורום, קבצים עם תמונת פרופיל שלהם או מסמכים, יש להכל מכנה משותף – אנחנו נותנים לגולש אפשרות לשים קובץ ב FileSystem של השרת שלנו.
כל מתכנת עם ידע בסיסי באבטחה יודע שיש צורך לעשות איזושהי וואלידציה לסוג הקובץ שמועלה, לברר שזה סוג שאנחנו מוכנים לקבל. אבל, תמיד כדאי לנקוט כמה צעדי ביטחון נוספים, למקרה שאיכשהו הייתה בעייה, הוואלידציה לא הייתה תקינה, יש פירצת אבטחה באפליקציה אחרת בשרת שמאפשרת איכשהו לשתול את הקובץ וכו’.
יש מספר צעדי ביטחון שאפשר לנקוט ברמת השרת כדי להגביל את הנזק במקרה כזה.
אז המקרה המדובר, היא אפליקציה שמאפשרת העלאת קבצים. המטרה: להגביל את הנזק שניתן לגרום במידה וניתן להעלות את כל סוגי הקבצים. השיטה: קצת משחק עם קונפיגורציה.
הדבר הראשון הכי חשוב, היא שקבצים של משתמשים מועלים לתיקייה המיועדת רק להם, תיקייה שבה אין שום דבר אחר. אנחנו לא רוצים לפגוע בפונקציונאליות של דברים אחרים תוך כדי התהליך.
בעוד שלאפליקציית ASP.NET רגילה נדרשות מעט מאד הרשאות בשביל לרוץ (בד”כ מספיק Read), לתיקייה הספיציפית אליה מועלים הקבצים נרצה לתת הרשאות Write בנוסף, מסיבות מובנות. לשם כך, צריך לתת הרשאות לחשבון Network Service ב IIS6 (בעבר זה היה בחשבון ASPNET). אם הפעלתם את אפשרות ה impersonate, אז במידה ומשתמש אנונימי זה יהיה המשתמש IUSR (המשתמש האנונימי של IIS). כדי לעשות את זה, פשוט ב properties של התיקייה, תוודאו שאין ירושה של הרשאות מתיקיות האב ותגדירו את ההרשאות המתאימות רק ליוזרים הנחוצים.
השלב הבא, הוא להגדיר ב iis. הצילומי מסך פה הם מ IIS6 – מטעמי נוחות. דרך ה IIS Man
agement Console (בקיצור, inetmgr), לכו ל website של האתר שלכם, הקליקו ימנית בנחישות על התיקייה בה יהיו הקבצים של המשתמשים, בחרו ב properties ובטאב Directory במקטע Application Settings תלחצו על Create. בשלב הזה למעשה הפרדתם את התיקייה הזאת ל”אפליקציה” נפרדת, ככה שיהיה אפשר להגדיר לה הגדרות שונות בצורה ספיציפית.
בנוסף, באותו המקום, יש להגדיר ב Execute premissions כ- None. בסופו של דבר, שום דבר לגיטימי לא יכול לרוץ מהתיקייה הזאת.
לאחר מכן, לחצ על הכפתור Configuration הנמצא באותו מקום. בחלון שנפתח, הטאב הראשון (שפתוח אוטומטית) הוא Mappings. למעשה, יש שם את המיפוי של סיומות הקבצים השונות ל ISAPI Extension המתאים (במילים אחרות – המנוע שאחראי להריץ אותם). ייתכן שבאפליקציה שלכם יש שימוש ב ISAPI’s שונים משלכם, או שאתם משתמשים בדיפולט – בכל מקרה, אין שום דבר שאתם באמת רוצים שירוץ מהתיקייה הזאת. אז פשוט תעשו Remove לכולם.
כעת, לאחר שהגדרתם את כל ההגדרות כפי שכתבתי, אם מישהו ינסה להיכנס לקובץ asp או aspx או כל קובץ אחר שמכיל סקריפט זדוני שלו שאיכשהו מצא את מקומו בתיקייה הזאת – זה פשוט לא יעבוד. קבצי ASP יחזירו שגיאת 404 (ליתר דיוק 404.2) וקבצי ASP.NET פשוט לא יפורשו ויוחזרו איך שהם ל client. בצורה הזאת, גם אם מישהו יצליח להעלות איזשהו shell דרך מנגנון העלאת הקבצים שלכם, הוא לא יצליח להריץ אותו.
בהצלחה.
-------------------------
<שורה תחתונה>שירה אחישר זכתה מקום שני בקטגוריה שלה בתחרות Intel ISEF. שחר גבירץ (אני) וידיד אלגאוי זכו מקום רביעי בקטגוריה שלהם ב Intel ISEF. אני מציע לכם לקרוא את כל הפוסט בשביל הפירוט המלא</שורה תחתונה>
-------------------------
אתמול בערב נחתה הטיסה שלנו בחזרה מכנס Intel ISEF 2009. מדובר בכנס שמציגים בו תלמידים לפני קולג’ ואוניברסיטה מכל רחבי העולם עבודות מחקר שהם עשו וזכו בתחרויות המקומיות שנערכות באיזור מגוריהם (במקרה שלנו, תחרות מדענים צעירים בירושלים).
השנה נערך הכנס בעיר רינו שבמדינת נבדה. רק כדי שתבינו עד כמה ההיערכות לכנס ענקית – 4 שנים לפני שעיר מסויימת מארחת את הכנס היא מקימה וועדה שהתפקיד שלה להכין את
העיר לכנס.
ל Intel ISEF 2009 נסעו מטעם ישראל 3 תלמידים (מימין לשמאל בתמונה: שירה אחישר, אני וידיד אלגאוי [השותף שלי לעבודה]) וכמה מורים שמשתתפים בסדנאות מיוחדות למורים הנערכות במקביל לכנס. בתור Adult in charge התלוותה אלינו ירדן, שבין השאר גם מדריכה במוזאון המדע בירושלים.
המסע לרינו הוא לא מסע פשוט. במוצ”ש שעבר (10 במאי) עלינו לטיסה שיצאה מנתב”ג לאטלנטה. משם, עלינו על טיסה שהסיעה אותנו לנמל התעופה בלוס אנג’לס, ששם התעכבנו במשך כמה שעות טובות הכוללות שעתיים של
דיליי בטיסה בגלל אובר-בוקינג (כנראה לא הודיעו להם שיש כנס מדעי ברינו שהרבה רוצים להגיע אליו, ככה שיהיו מעט ביטולים) ובגלל תקלה טכנית בהמשך. שם גם פגשנו חבר’ה נוספים שהיו בדרך לISEF (בתמונה משמאל, מדברים, מימין לשמאל: שירה אחישר מישראל ואלכס ומיטשל מפלורידה).
הגענו לרינו (בערך שעת טיסה מLA במטוס שיותר דומה לאוטובוס שהדביקו לו כנפיים). הדבר הראשון שרינו משדרת לך כשאתה מגיע אליה הוא “פה מגיעים בשביל להמר”. נמל התעופה מלא במכונות הימורים (באירועים באייסף הציגו עובדוות מעניינות על רינו פה ושם.
לפי אחת מהעובדות, על כל עשרה תושבים יש סלוט של מכונת הימורים), שכמובן לגיל 21 פלוס (והאמריקאים הם מאלה שנדבקים לתקנות), ככה שזה לא היה ממש רלוונטי לנו. כמה שזה נראה ככה, התמונה משמאל לא צולמה בקזינו אלא בנמל התעופה של רינו. אם תלחצו על התמונה להגדלה אפילו תוכלו לראות את השלט שמפנה אתכם ל Gate C4 של חברת התעופה הוריזון.
אירוע מסורתי שמתרחש כחלק מ Intel ISEF נקרא ה Pin Exchange. באירוע הזה מחליפים סיכות של מדינות. כל מדינה מציידת את המתחרים שלה בסיכות שונות, חלקן מגניבות יותר וחלקן מגניבות פחות (*אהמ*). זאת גם הזדמנות לראות אנשים מכל מיני מקומות בעולם, וזאת הזדמנות מעולה להבהיר נקודה חשובה: אל תאמינו לאף אחד שאומר לכם שמצב ההסברה של ישראל בעולם גרוע. אל תאמינו לאף אחד שאומר לכם ששונאים אותנו ושרק מייחלים למותנו. נכון, יש כאלה מקומות. אבל יש גם מקומות (Intel ISEF) שבהם כשאמרתי שאני ישראלי קיבלתי חיבוקים. לא מנטליים. פיזיים. לא נראה לי שיש מדינה אחרת שמישהו קיבל חיבוקים כשהוא אמר מאיזה מדינה הוא. דרך אגב, פגשנו שם גם חבר’ה מלבנון שהיו אחלה.
המקום שבו מוצגים הפרוייקטים הוא מקום ענק. בעצם, ענק לא מתאר מספיק
טוב את הענקיות של המקום הזה. תחשיבו על שטח, שכולל כמה אולמות ע-נ-ק-י-י-ם, כאשר באולם אחד מוצגים כ-1500 פרוייקטים ואולם אחר שימש לאירועים של כל הקהל (כמו טקסי הפרסים, טקס הפתיחה וכאלה).
היום השני מוקדש ל setup של ההכנות. לנו ספיציפית היה רק לשים פוסטרים,
אבל יש כאלה שיש להם גם המחשות שונות ומשונות. עכשיו שתבינו. מדובר באמריקאים. מה שאומר שעוד בארץ מילאנו טופסיאדה מפה ועד להודעה חדשה. בגלל שאנחנו השתמשנו בפ רוייקט שלנו בעופרת, אז כמובן היינו צריכים טופס ש מסביר איך היינו זהירים בעבודה עם חומר מסוכן. שירה שהעבודה שלה כללה בני אדם, הייתה צריכה למלא טופס של עבודה בבני אדם ולצרף אישורים. כשמגיעים לשם, מפורסם violation list, שמכיל פרוייקטים שלא מקבלים אישור להתחיל את הכנת הפרוייקטים שלהם בגלל הפרות של כללי האתיקה, מילוי שגוי של הטפסים וכל דבר שהמוח הישר להפליא של האמריקאים מסוגל לחשוב עליו. למזלנו, נחסכו מאיתנו הוועדות של ההסבר, כי לכל אחד מאיתנו המתין טופס על השולחן
עם החותמת הזהובה שמאשרת את התקנת הפרוייקט. דרך אגב, אחרי ששמים את התצוגה, יש גם וועדה שעוברת על הפרוייקטים ועל התצוגה לראות שהכל תקין מבחינה בטיחותית ומבחינת תקנות נוספות, ורק אז מקבלים אישור סופי. בתמווה התחתונה משמאל תצוגה לפני שהיא קיבלה אישור (המוזר ששוכב על השולחן זה אני). בשביל לקבל את האישור, דרך אגב, היינו צריכים להפוך את הזווית שבין הפוסטר המרכזי לצדדיים ליותר ישרה, כדי שהם לא ייצאו מחוץ לתחומי השולחן. בתמונה העליונה משמאל, הראש שלי והראש של שירה בזמן שאנחנו מדביקים דבק דו”צ (המצאה גאונית!).
ביום השני בערב מתקיים ה opening ceremony, טקס הפתיחה. לטקס הזה בד”
כ מביאים מישהו מסלבריטאי האיזור להנחות אותו, במקרה שלנו מגישת החדשות של ערוץ הטלוויזיה הנצפה בנבדה. מישהי אנרגטית ברמה מטורפת. קצת אפילו יותר מדי אנרגטית. מפחד שפעת החזירים, אינטל החליטו שבכנס הזה מבוטלות לחיצות הידיים (מה שעדיין התקיים בפגישות בין אנשים, אבל לא באירועים רשמיים) ומחליפים אותם בהשקת מרפקים אחד של השני. זאת גם נקודה טובה לציין שבכל מקום היו מכשירים עם נוזלי סטרליזיציה (אפילו עם התיקים של הכנס חילקו משחות וחומר לסטרליזיציה של הידיים). בתמונה משמאל, אני מדגמן מכשיר לחיטוי הידיים שהוצב בפני הכנסה לחדר המחשבים (שהשימוש במכשיר חובה לפני הכניסה) והשלט שעליו אומר Share
your genius, not your germs. בכל אופן, גם טקס הפתיחה מאד מרשים, ובמסגרתו רצים נציגים מכל משלחת (במקרה של ישראל, פשוט שלושתנו, מכיוון שהמשלחת שלנו קטנה) עם שלטים שמייצגים את המדינה (ששירה הכינה, ואנחנו בעיקר ניסינו לצבוע. עם דגש על הניסינו). בתמונה משמאל אתם יכולים לראות גם את משלחת ישראל (כולל ירדן, ה”מבוגר האחראי”) עם הפוסטר שלה. דרך אגב, את הגבעול השמאלי בהתחלה אני צבעתי, מה שהצריך אח”כ את שירה ואת ידיד לעשות בקרת נזקים, שכללה ציור מחודש שלו וצביעה מחודשת (אבל את ה SHALOM הצלחתי לצבוע [פחות או יותר] טוב).
ביום השלישי היינו בשני shoptalks (סוג של הרצאות). הראשונה על השיפוט והשנייה על Talking Science (שהיה ממש מוצלח, איך לדבר על נושאים מדעיים להדיוטות, איך לדבר בפני קהל, איך להבליט את הדברים הטובים. ממש טוב, וכולל דוגמאות וידאו של נכון ולא נכון מראיונות אמיתיים שאנשים נתנו). אח”כ גם היינו
ב downtown reno (או שזה היה ביום הרביעי, אני לא כ”כ זוכר). downtown reno הוא למעשה נהר שזורם שם וסביבו יש כל מיני פארקים קטנים ושטחי דשא קטנים שבהם אנשים יושבים, מתרחצים קצת בנהר עם הכלבים שלהם וסתם נהנים. ממש היה כיף לראות את זה. אח”כ, גם רצינו לראות קזינו מבפנים (בתמונות משמאל).
תאמינו לנו שלא הימרנו? בעצם, אתם לא צריכים. האמריקאים, גם אם היינו רוצים, לא היו נותנים לנו.
השיפוט בתחרות הזאת עובד בצורה הבאה: יש שני סוגי פרסים. האחד, נקרא Special Awards ומוענק ע”י ארגונים שונים בהתאם לשיקול הדעת שלהם לפרוייקטים שהם אהבו. חלק גדול מהפרסים מהסוג הזה הם לאמריקאים בלבד, כי הם מוענקים ע”י הממשל האמריקאי וזרועותיו (הצבא, משמר החופים, הצי, חיל האוויר) או כי הם מלגות המוענקות ע”י אוניברסיטאות אמריקאיות. הסוג השני, שהוא הסוג העיקרי, נקרא Grand Awards. פה מדובר על פרסים ששופטים בתחרות מעניקים. וגם פה יש מורכבות. העבודות בתחרות מחולקות לפי קטגוריות. בכל קטגוריה נקבעים כמה זוכים במקום רביעי, כמה במקום שלישי, כמה במקום שני וכמה במקום ראשון ובנוסף ה Best of Category. בנוסף, נקבעים זוכים במקום הראשון, השני והשלישי הכולל של התחרות. בד”כ, חלק גדול מהזוכים הם כאלה שהיו כבר בעבר ב ISEF (יש הרבה שמגיעים ל ISEF כמה שנים, חלק כבר 4 שנים ברציפות. מט’ ועד י”ב, אבל בישראל זה לא קורה). מכיוון שהפרוייקט שלי ושל ידיד הוא בצוות, השיפוט אצלנו יותר מורכב. יש קטגוריה שהיא למעשה כל העבודות שנעשו בצוות, כאשר השופטים של הקטגוריה הנושאית של העבודה (במקרה שלנו, Environmental Science) מעבירים את הניקוד לשופטים של ה team projects שהם משווים בין כל העבודות וקובעים את הדירוג בקטגוריית ה Team Projects.
היום הרביעי למעשה היא יום השיפוט. השיפוט הלך בסדר לכולנו, עם שופטים מסוגים שונים. במבחן התוצאה, כנראה שלכולנו היה טוב.
ביום הרביעי בערב הייתה מסיבה שגם היא הייתה נחמדה מאד.
יום חמישי הוא יום פתוח לקהל. מגיעים תלמידים מבתי ספר ברחב
י רינו והסביבה, מקבלים דפי עבודה (שאחת השאלות היא לציין פרוייקט שנעשה ע”י מישהו שאינו מארה”ב, ומה שפת המקור של אותו אחד, שאלה ששמחנו לענות עליה), מסתובבים בין הפרוייקטים ועונים עליהם. ביום הזה שמים כמו חבל כדי למנוע מאנשים להיכנס לפרוייקטים בלי רשות, ועל החבלים תולים את השלטים מלאי ההומור שאתם רואים משמאל. לא נרשמו נפגעים מנשיכות באותו היום, בעיקר כי האנשים לא האכילו אותנו. מנומסים האמריקאים האלה. אגב כך, בטיסה הלוך לארה”ב לקחתי איתי חבילת ערגליות שהלחיצה את אחת הנשים בשדה התעופה מה homeland security ששיקפו את התיקים. כך שהגיע איזשהו איש אבטחה, הוציא לי את הכל מהתיקים עד שהגיע לערגליות, שאל אותי אם זה ממתק שאוכלים בישראל ואפילו שיקף (!) את החבילה הזאת. אחרי שראיתי איך הערגליות נראות שמשקפים אותם, אני יכול להבין למה היה לו קצת קשה לקבל את העובדה שזה
אוכל שאוכלים פה. ביום חמישי בערב היה טקס הענקת ה Special Awards שבו לא היו לישראל הישגים כ”כ, בין השאר, כי כפי שפירטתי קודם, קהל היעד של חלק גדול מהפרסים האלה הוא אמריקאים. ביום הזה גם הלכנו לאכול במסעדה הצמחונית היחידה ברינו, שנמצאת בבניין דירות ויש לה את התפריט כי משעשע שראיתי בחיי (קטגוריות בתפריט למשל: American Food with Italian Names, American Food with Dumb Names). צילמתי את התפריט, לצערי חלק מהתמונות לא יצאו כ”כ באיכות קריאה (התמונה שצירפתי קריאה, אם תקליקו עליה ותראו את הגודל המלא). האוכל, דרך אגב, מאד טעים (פסטה. יאמי).
היום השישי הוא יום הכרזת ה Grand Awards. שירה זכתה מקום שני בקטגוריה, ומה שבאמת מפתיע לאור הפסימיות שלי בעניין זה שאני וידיד זכינו מקום רביעי בקטגוריה של ה Team Projects. בהחלט נחמד.
נשארנו ברינו לשבת, וביום ראשון בבוקר חזרנו לארץ. במסלול רינו—>Salt Lake City—>אטלנטה—> תל אביב. גם פה לא היה לנו יותר מדי מזל, והייתה תקלה טכנית במטוס שהיה אמור לטוס לאטלנטה, דבר שגרר את זה שהחליפו מטוס למטוס נוח יותר מהמטוס המקורי, ושהגענו לאטלנטה באיחור של 45 דקות, מה שלמעשה גרם שנאלצנו ללכת מהר מאד כדי לוודא שנספיק לטיסה לארץ. הדבר שבאמת מפתיע בכל הטיסות הללו, זה שבאף אחת מהן לא אבדו לנו המזוודות (לאף אחד מאיתנו). בהחלט יתרון חשוב. הגענו לישראל ביום שני (אתמול ) בשעות הערב. אם לסכם בשני תיאורים את הISEF הזה – כיף ומוצלח בהחלט יעשו את העבודה.
שיהיה לכולכם יום מעולה.
שחר.
נ.ב. מה מחלקים Intel בתיקי הכנס? הרבה נוזלי חיטוי לידיים (שפעת החזירים), הרבה חומרים פרסומיים לא מעניינים, וגם, שימו לב, עט שמאיץ באוכלוסיית המצוגה שמגיעה לכנס להימנע מלקחת סמים. 
לאחרונה (כמה שעות אחרי שיצא למנויי טקנט) התקנתי את Windows 7 RC על המכונה העיקרית שלי. החלטה אימפולסיבית, שלא ממש מתיישבת עם הרושם שקיבלתי מהבטא של Windows 7 (למרות שזה היה אז על מחשב אחר). ההחלטה הזאת התבררה כהחלטה חכמה ביותר. מאד חכמה. במילים חרות, אני עכשיו עם Windows 7 ונהנה מכל רגע. זה לא שבויסטה דברים לא רצו. אבל פה הם רצים הרבה הרבה יותר מהר.
צריכת הזיכרון קצת יותר טובה (בערך 600 מגה פחות מבויסטה), אבל המערכת מרגישה הרבה הרבה יותר מהירה. חוץ משיפורי הביצועים, גם השיפורים הויזואליים חביבים ביותר, המערכת נוחה (לא חשבתי שהרעיון שכל אפליקציה יכולה לשים כמה קיצורים משלה שיהיו זמינים בתפריט ה start כשעוברים ליד האפליקציה יתגלה כמשהו כ”כ שימושי) והיכולת לעשות mount ל VHD מתגלה כדבר מעולה.
מה לא אהבתי?
דבר ראשון, ה taskbar החדש קטסטרופלי. על הפנים. לא נוח בכלל. ניסיתי להסתגל אליו איזה יומיים ואז הבנתי שחבל על המלחמה שלי, אמרתי לו Never Combine, הגדלתי את הגודל לשתי שורות (כמו שאני רגיל) ובא לציון גואל.
דבר שני, יש באג מרגיז מאד עם מסנג’ר פה, שגורם לכך שגם כשהחלון הראשי של המסנג’ר סגור יש ב taskbar (לא ב tray bar, איפה שצריך להיות) סימן כמו של חלון, שמופיע בו השם שלי והסטטוס שלי וכשאני לוחץ עליו הוא פותח בנוסף אליו גם את חלון אנשי הקשר. אם אני יוצא ממנו, זה כאילו לעשות exit למסנג’ר. בקיצור, משהו שמלכלך לי את ה taskbar.
דבר אחרון, הפיצ’ר שכשאתה מעביר את העכברע ל משהו ב taskbar הוא כאילו מראה לך דרך כל החלונות את החלון המסויים הזה לא ממש נוח כשעובדים עם שתי מסכים, כי הוא לא יודע לעשות את המצב הזה רק למסך שבו נמצא באמת החלון, אלא הוא עושה את זה (מעביר למצב שקוף) את כל החלונות, בשני המסכים.
לסיכום, לא רואים פה שינויים מרחיקי לכת מבחינת חווית השימוש. אבל המערכת יעילה יותר, טובה יותר, בוגרת יותר, עם פחות הטרדות ולמעשה התגברה על כל הבעיות הקטנות שהיו פה ושם בויסטה.
שחר.
More Posts
Next page »