DCSIMG
January 2009 - Posts - Ido Flatow's Blog Veni Vidi Scripsi

Ido Flatow's Blog

Veni Vidi Scripsi

News

Have you heard me speak?
Powered
<style type='text/css' media='screen' id='sm_css'> #smix {overflow: visible;height: auto;border-radius: 10px;max-width: 250px;background-color: #323232;text-align: left;font-size: 12px;line-height: 16px;font-family:'Lucida Sans Unicode','Lucida Grande',Verdana,Arial,Helvetica,sans-serif;-webkit-border-radius: 10px;-moz-border-radius: 10px;border-radius: 10px;} #smix a {color: #0056CC;text-decoration: none;} #smix .sm_head {color: #fff; line-height: 1em;font-size: 1.4em;padding: 10px;color: #fff;} #smix .sm_lanyard_wrapper {background-color: #fff;;clear: both;width: 97%;margin: 0 auto;margin-bottom: 0px;} #smix .sm_lanyard_content {padding: 7px;}#smix button.sm_rec, #smix a.sm_rec, #smix input[type=submit].sm_rec { padding: 6px 10px; -webkit-border-radius: 2px 2px;-moz-border-radius: 2px; border-radius: 2px; border: solid 1px rgb(153, 153, 153); background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(rgb(255, 255, 255)), to(rgb(221, 221, 221))); color: #333; text-decoration: none; cursor: pointer; display: inline-block; text-align: center; text-shadow: 0px 1px 1px rgba(255,255,255,1); line-height: 1; }#smix .sm_rec:hover { background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(rgb(248, 248, 248)), to(rgb(221, 221, 221))); }#smix .sm_rec:active { background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(rgb(204, 204, 204)), to(rgb(221, 221, 221))); }#smix .sm_rec.medium { padding: 3px 7px; font-size: 13px; }#smix .sm_rec span.icon.thumbs_up {background-position: 0px 36px;vertical-align: text-top;display: inline-block;margin-right: 4px;height: 18px;width: 16px;background-image: url(http://speakermix.com/images/new/thumbsold.png);}#smix .sm_rec:hover span.icon.thumbs_up {background-position: 0px 18px;} #smix .sm_events {padding:2px 0px 4px 0px;} #smix .sm_section {font-size: 10px; border-bottom: 1px solid silver; margin-bottom: 6px;} #smix .sm_subline {font-size:120%;margin-top:4px;font-weight:bold} #smix .powered {text-align: right} #smix .powered img {margin: 7px} </style>
Sela Technology Center

Advertisement

January 2009 - Posts

Working with binary data types and detached entities in entity framework

Entity framework supports creating an entity type that holds binary data (byte[]), this is useful if you need your entity to hold things like file streams, photos etc.

But there is a problem when your entity has a property of type binary.

If you’ve worked with EF and N-tier applications, you know that you can serialize your entity and pass it to the client, deserialize it the client-side, change it and the send it to the server to be updated.

MS suggests using the ApplyPropertyChanges method of the ObjectContext to apply the changes made to the entity onto the current entity, thus making sure that no unnecessary updates will run in the DB unless something has changed in the entity.

The way the method does this is simply by comparing each property with its original value (call the Equals method on both properties) – this works for almost every primitive type (string, int, boolean, datetime) but not on binary types, because it uses Equals to compare the two arrays instead of comparing its data, so actually every time you use this method on an entity that has a binary field the entity is marked as changed.

Until the EF team fixes this bug (probably in V2), here’s a simple method you can call after calling ApplyPropertyChanges, in order to reset the state of entities which weren’t really changed.

static HashAlgorithm hash = new MD5CryptoServiceProvider();

private static void ApplyBinaryProperyChanges(ObjectContext model, EntityObject entity)
{
    bool entityModified = false;
    // Get the state of the entity and its metadata (to find the type of the changed properties)
    ObjectStateEntry state = model.ObjectStateManager.GetObjectStateEntry(entity.EntityKey);
    ReadOnlyMetadataCollection<EdmProperty> entityProperties = (state.EntitySet.ElementType as EntityType).Properties;

    if (state.State == EntityState.Modified)
    {                
        foreach (string property in state.GetModifiedProperties())
        {
            // Find the primitive type that describes this property
            PrimitiveType propertyMetadata = entityProperties[property].TypeUsage.EdmType as PrimitiveType;
            if (propertyMetadata != null && propertyMetadata.PrimitiveTypeKind == PrimitiveTypeKind.Binary)
            {
                // Get the byte array from the entity (current & original)
                byte[] newArr = state.CurrentValues[property] as byte[];
                byte[] orgArr = state.OriginalValues[property] as byte[];

                // Init null values since hash needs to work on initiated arrays
                if (newArr == null)
                    newArr = new byte[0];
                if (orgArr == null)
                    orgArr = new byte[0];

                // Compare hashes for both arrays 
                // (if both null, they will return same hash because both arrays are 0 length arrays)
                if (Convert.ToBase64String(hash.ComputeHash(newArr)) !=
                    Convert.ToBase64String(hash.ComputeHash(orgArr)))
                {
                    // Checksum isn't the same - entity was modified
                    entityModified = true;
                    break;
                }
            }
            else
            {
                // Some other primitive property was modified - no need to check further
                entityModified = true;
                break;
            }
        }

        if (!entityModified)
        {
            // entity wasn't modified (all modified properties are actually binary data which wasn't modified)
            state.AcceptChanges();
        }
    }
}

קבלת שם קובץ בעברית מ-Http Handler

לא פעם יצא לי לעבוד עם מערכות asp.net אשר צריכות לאפשר שמירה והצגה של קבצים מצורפים (attachments) – לפעמים מדובר בתמונות נלוות לפריט מידע, לפעמים במסמכים או בכל קובץ אחר.

במקרים בהם עובדים במערכת WEB, לא נהוג שהמערכת תחזיר UNC למיקום הקובץ ברשת, אלא המערכת צריכה להחזיר את הקובץ עצמו, באמצעות URL חד-ערכי שמזהה את הקובץ.

אבל, במרבית המערכות הקובץ לא ישמר על שרת ה-IIS, ככה שניתן לו URL משלו, אלא הקובץ ישב בשרת קבצים של-IIS יש גישה אליו, או אף אולי ב-DB, ויש צורך ליצור HTTP Handler על-מנת לאחזר את הקובץ.

אז מה עושים ? כותבים Handler שמגיע לקובץ/DB וטוען אותו בינארית ל-Response. מי שניסה לעשות זאת נתקל בבעיה הקטנה, שכאשר הקובץ מגיע לדפדפן, ומופיעה הההודעה המוכרת של האם לשמור או לפתוח, שם הקובץ המוצג (וזה שניתן כברירת מחדל לשמירה) הוא שם ה-ASHX עצמו ולא שם הקובץ אותו טענו ל-Response.

טיפ 1 – על-מנת לקבוע את השם שיוצג בדפדפן כשם הקובץ, יש להוסיף את השורה הבאה

context.Response.AddHeader(
    "content-disposition", 
    "attachment; filename=\"" + fileName + "\"");    

השורה הזו עובדת מצויין על שמות קבצים באנגלית, לדוגמה:

image

אבל אם ננסה לתת שם בעברית, ההודעה תראה כך (שימו לב לשם הקובץ):

image

טיפ 2 – על-מנת לאפשר הצגה של שמות הקבצים בעברית, יש להפעיל על המשתנה את המתודה UrlPathEncode באופן הבא:

fileName = context.Server.UrlPathEncode(fileName);

ואז השם יופיע טוב:

image

Attaching events to Silverlight's DataTemplate

If you've used DataTemplates in Silverlight 2, you've noticed that when defining it in a resource of a control, the attached event should be placed in the .cs file containing that control.

If it's a UserControl, it is logical for it to be in the usercontrol's .cs file, usually either coding for that event in the user control itself or exposing the event as an event exposed by the user control.

So far so good, but what happens if we have a general DataTemplate that is used in multiple control / user controls / pages ... ? the obvious answer is place it the app.xaml, but what will happen if we want to catch events from the contained controls in the DataTemplate? putting the event code in the app.xaml.cs is the only way to do it, but it isn't logical to put event code in the app.xaml.cs which will refer to a usercontro/page, isn't it so?

One way we can solve this problem is to define the DataTemplate in a new user control, wrap the event with a new event of the usercontrol's raise that event accordingly.

If you choose to create a UserControl for every DataTemplate or create a “Templates” UserControl for all DataTemplates that have a common usage, it's up to you.

I've attached an example demonstrating this