DCSIMG
October 2011 - Posts - שלמה גולדברג (הרב דוטנט)

שלמה גולדברג (הרב דוטנט)

מרצה בסלע ויועץ בעולם ה - net.

October 2011 - Posts

Delete Entity - Entity Framework (4.0 - 4.1)

 

העבודה עם Entity Framework הקילה עלינו את החיים וחסכה מאיתנו את הצורך לכתוב שכבת DAL.
 
אחד הדברים שהיינו דואגים לעשות ב - DAL היה למחוק שורה במידה והקשר נמחק - כלומר במידה ויש לנו אובייקט Company המכיל הצבעה לאובייקט Hit, במידה והקשר נמחק (כלומר Company מצביע ל - Hit אחר) ה - Hit המקורי אמור להמחק.
 
 
ב - Entity Framework גרסה 4.0 זה די פשוט.
 
ראשית נמצא את האובייקט שנרצה למחוק
 

var obj = context.Companies.First().Hit;

 
כעת במידה והקשר מוגדר EndOnDelete - Cascade (ב - designer של Entity Framework על הקשר) חייבים לפני המחיקה למחוק ידנית את הקשר (אחרת זה ימחק גם את ה - Company)
 

context.Companies.First().Hit = null;

במידה וזה מוגדר ב - none ניתן לדלג על שורה זו.
 
כעת רק צריך למחוק את האובייקט באחד מהדרכים הבאות:
 

context.DeleteObject(obj);

context.Hits.DeleteObject(obj);

 
וכעת רק צריך לשמור.
 

context.SaveChanges();

 
 
ב - Entity Framweork 4.1 זה קצת יותר מסובך, אין לנו פונקצייה בשם Delete, על ה - context.
 
יש לנו על אובייקט מסוג Set פונקציית Remove, אבל בדרך כלל כשנעבוד עם Entity Framework 4.1 לא נחשוף את כל האובייקטים כ - Set אלא רק את אלו שמוגדרים כ - Entitiy - (לדוגמא כשנעבוד ב - DDD).
 
 
לכן נוכל להשתמש בפונקציות הבאות:
 

public void ChangeState(object entity, EntityState state)

{

    if (entity != null)

    {

        var entry = Entry(entity);

        entry.State = state;

    }

}

 

public void DeleteObject(params object[] entities)

{

    foreach (var item in entities)

    {

        ChangeState(item, EntityState.Deleted);

    }

}

 
 
הפונקציה ChangeState יודעת לקבל אובייקט וה - State החדש שלו, היא משתמשת בפונקציה Entry של DbContext כדי לקבל אובייקט של Entity Framework והיא משנה לו את ה - State.
 
הפונקציה DeleteObject מקבלת מערך של אובייקטים ומפעילה על כל אחד מה את ChangeState.
 
ברור שבמידה ואנחנו מוחקים אובייקט שאנחנו חושפים לו Set מתוך ה - Context נוכל להשתמש בפונקציית Remove
 

Companies.Remove(obj)

background-position RTL jQuery plugin

כיצד להגדיר background-position כשמדובר באתרים עם rtl ? 
 
התחלתי לעצב אתר כשקבלתי עיצוב מותאם לאנגלית והייתי צריך לשנות אותו לעברית,
 
על ההתחלה נתקלתי בבעייה הבאה:
 
נקח לדוגמא את ה - css הבא:
 
 

body

{

    background-image: url('Penguins.jpg');

    background-repeat: repeat-y;

    background-position-x: 30px;

}

 
css פשוט ביותר שיגרום לתמונה מסויימת להתחיל בשלושים פיקסל ימינה, מה יקרה עם נוסיף את השורה הבאה:
 
 

direction: rtl;

 
האתר יעבור לצד ימין, כשתמונת הרקע נשארה תקועה בצד שמאל, בעוד שהכוונה כרגע שמצד ימין של האתר יהיה רווח של שלושים פיקסל לשמאל.
 
(פתרון ביניים הוא לעבוד באחוזים (95%) אבל במקרה כזה מיקום התמונה עלול להשתנות בשינוי גודל החלון)
ב - css3 כבר תיקנו את הבעייה על ידי הקוד הבא:
 

background-position-x: right 30px;

 
אבל עד כמה שידוע לי הדפדפן היחיד שתומך בזה הוא (נכון להיום) IE9.
 
חפשתי הרבה זמן בגוגל אך לא מצאתי פיתרון, בסופו של תהליך הגעתי למסקנה שעלי לכתוב סקריפט שיזיז את התמונה בהתאם למיקום החלון.
 
אתמול קראתי בפוסט של שי רייטן על כתיבת jQuery plugin - החלטתי לנסות, והנה התוצאה.
 

 

(function ($) {

    $.fn.backgroundPositionRTL = function (imgWidth, rightPosition) {

        var $element = this;

 

        $(window).resize(function () {

            changePosition();

        });

 

        (changePosition = function () {

            var position = ($(window).width() - (imgWidth + rightPosition)) - $element.position().left;

            $element.css('background-position-x', position + 'px');

        })();

    }

})(jQuery);

 

 

 

 
בקריאה לפונקציה (ע"י סלקטור כלשהו) נשלח את גודל התמונה וכמה להזיז מימין.
 
נרשם לאירוע של שינוי גודל החלון ונפעיל את הפונקציה שיודעת לשנות את המיקום בהתאמה, (בסוף הפונקציה של changePosition יש () וזהו כדי להפעיל בפעם הראשונה את הסקריפט לפני שהיה שינוי בגודל החלון.
 
כעת השימוש יהיה בצורה הבאה:
 

$(document).ready(function () {

    $(document.body).backgroundPositionRTL(410, 30);

});

 
 
עוד דוגמא לשימוש:
 
 

.div1

{

    background-image: url('Penguins.jpg');

    background-repeat: no-repeat;

    background-position-x: right 50px;

    width: 600px;

    height: 500px;

    border: 1px solid black;

}

 

$('.div1').backgroundPositionRTL(410, 50);

File Upload: Cannot access a closed file.

 

אם יצא לכם לכתוב פקד המאפשר להעלות קבצים לשרת, שומר אותם ב - Session ומתי שהוא תנסו לקרוא את התוכן של הקבצים, ייתכן שתקבלו את השגיאה:
 
Cannot access a closed file.
 
 
בפוסט זה אני אדגים כתיבה של פקד כזה (פקד פשוט מאוד - עבור הדוגמא) וכיצד לפתור את הבעייה.
 
נכתוב דף בשם uploader.aspx,
 
קוד ה - html:
 

<form id="form1" runat="server">

<div>

    <asp:FileUpload runat="server" ID="fu1" />

    <asp:Button Text="Upload" OnClick="upl_click" runat="server" />

</div>

</form>

קוד צד שרת:
 

protected void upl_click(object sender, EventArgs e)

{

    List<HttpPostedFile> list = (List<HttpPostedFile>)Session["files"];

    if (list == null)

    {

        list = new List<HttpPostedFile>();

        Session["files"] = list;

    }

 

    list.Add(Request.Files[0]);

 

    Response.Write("<script>parent.uploaded('" + Request.Files[0].FileName + "')</script>");

}

 
ראשית נקבל את המקום שבו אנחנו שומרים את כל הקבצים (אם לא קיים נייצר אותו).
לאחר מכן נוסיף את הקובץ החדש,
ובסוף נריץ סקריפט שיפעיל פונקצייה בדף שמארח את הפקד. (ושוב אני אדגיש - זהו קוד בסיסי ופשוט ביותר - רק כדי להראות כיצד להתגבר על התקלה)
 
 
נניח שיש לנו דף המכיל את הפקד (נשתמש ב - colorbox כדי להציג אותו בצורה טובה יותר)
קוד ה - html:
 
בהתחלה נוסיף את קבצי הסקריפט והעיצוב הנדרשים:
 

<script src="jquery-1.6.4.js" type="text/javascript"></script>

<script src="jquery.colorbox.js" type="text/javascript"></script>

<link href="colorbox.css" rel="stylesheet" type="text/css" />

 
כעת נכתוב את ה - html הבא:
 

<form id="form1" runat="server">

<div>

    Files:

    <ul>

    </ul>

    <asp:Button Text="Save" OnClick="save_click" runat="server" />

    <input type="button" value="Add File" onclick="addFile()" />

</div>

</form>

 
בזמן לחיצה על Add File נפעיל את הסקריפט הבא:
 

function addFile() {

    var a = $(document.createElement('a'));

    a.attr('href', 'uploader.aspx');

 

    a.colorbox(

    {

        iframe: true,

        opacity: '0.55',

        height: '200',

        width: "400",

        open: true

    });

}

 
הסקריפט יודע לייבא את התוכן של דף uploader כ - iframe ולהציג את החלון.
 
כשהמשתמש ילחץ על הלחצן "Upload" בדיאלוג שייפתח לו, אמור לרוץ פונקציה בשם uploaded (כפי שראינו בקוד צד שרת של uploader)
 
 

function uploaded(fileName) {

    var li = document.createElement('li');

    li.appendChild(document.createTextNode(fileName));

    document.getElementsByTagName('ul')[0].appendChild(li);

 

    $.colorbox.close();

}

 
 
כעת בלחיצה על Save ננסה להריץ את הקוד הבא:
 

protected void save_click(object sender, EventArgs e)

{

    List<HttpPostedFile> list = (List<HttpPostedFile>)Session["files"];

    foreach (var item in list)

    {

        byte[] data = new byte[item.InputStream.Length];

        item.InputStream.Read(data, 0, data.Length);

    }

}

 
 
כאן נקבל את השגיאה של Cannot access a closed file.
 
לאחר קצת שיטוט בגוגל הגעתי לכאן, תוכלו לקרוא על ההבדלים בניהול הזכירון בין גרסאות 1.1 ומעלה, מבחינתנו המשמעות היא שקבצים מעל גודל מסויים לא נשמרים בזיכרון וכדי לפתור את זה נוכל להוסיף את השורה הבאה לקונפיג.
 

<httpRuntime maxRequestLength="10240" requestLengthDiskThreshold="4096" />

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

Cast from Generic type

 

אחד הדברים המפריעים בעבודה עם Generic Type, שאי אפשר לעשות להם casting לאובייקטים אחרים, לדוגמא:
 

static void MyFunc<T>(T item)

{

    Work((Entity)item);

}

 

static void Work(Entity item)

{

 

}

 
הקוד הא יזרוק שגיאה של:
 
Cannot convert type 'T' to 'ConsoleApplication1.Entity'
 
כדי לבצע בכל זאת המרה בין האובייקטים ניתן להשתמש ב - Convert.ChnageType בצורה הבאה:
 

static void MyFunc<T>(T item)

{

    Work((Entity)Convert.ChangeType(item, typeof(Entity)));

}

 
 
כדי לחסוך קצת זמן ולכתוב קוד בצורה יפה יותר, כתבתי את ה - Extension Method הבא
 

public static class ObjectExtension

{

    public static T ChangeType<T>(this object obj)

    {

        return (T)Convert.ChangeType(obj, typeof(T));

    }

}

 
 
וכעת ניתן לכתוב קוד כזה:
 

static void MyFunc<T>(T item)

{

    Work(item.ChangeType<Entity>());

}

 
 
 

שליחת מערך של מחרוזות ב - GET לשירות שנכתב ב - WCF המוגדר לעבודה עם REST

 

 
באחד הפוסטים בסדרת WCF הראיתי כיצד ניתן להגדיר שירותי WCF שיעבדו ב - REST ונוכל לגלוש אליו מהדפדפן בעזרת GET.
 
לפעמים אנחנו צריכים לקבל כפרמטר מערך של מחרוזות, במידה ונעשה את זה נקבל את השגיאה המפורטת, מה שאומר שהוא לא יודע כיצד להמיר את ה - query string למערך של מחרוזות.
 
Operation 'OPERATION' in contract 'CONTRACT' has a query variable named 'VARIABLE' of type 'System.String[]', but type 'System.String[]' is not convertible by 'QueryStringConverter'.  Variables for UriTemplate query values must have types that can be conve
 
מצאתי את הפתרון כאן, הרעיון הוא להגדיר Converter משלנו שיידע להמיר את ה - query string למערך של מחרוזת על ידי הגדרה מוסכמת מראש (פסיקים בין הערכים, או קבלת מחרוזת בפורמט JSON).
 
 
נתחיל בקוד שצריך לכתוב.
 
ראשית נירש ממחלקת WebHttpBehavior שהיא המחלקה הראשית המאפשרת rest ונשנה את המתודה שאחראית על החזרת ה - QueryStringConverter
 

public class CustomHttpBehavior : WebHttpBehavior

{

    protected override QueryStringConverter GetQueryStringConverter(OperationDescription operationDescription)

    {

        return new CustomQueryStringConverter();

    }

}

 
כעת נממש מחלקה משלנו שתדע להמיר מה - query string למערך של מחרוזות
שימו לב שבדוגמא אני מצרף דוגמא למימוש עם פסיק ומימוש עם json)
 

public class CustomQueryStringConverter : QueryStringConverter

{

    public override bool CanConvert(Type type)

    {

        if (type == typeof(string[]))

        {

            return true;

        }

 

        return base.CanConvert(type);

    }

 

    public override object ConvertStringToValue(string parameter, Type parameterType)

    {

        if (parameterType == typeof(string[]))

        {

            //with json option

            string[] parms = new JavaScriptSerializer().Deserialize<string[]>(parameter);

 

            //with comma delimited

            string[] parms = parameter.Split(',');

            return parms;

        }

 

        return base.ConvertStringToValue(parameter, parameterType);

    }

 

    public override string ConvertValueToString(object parameter, Type parameterType)

    {

        if (parameterType == typeof(string[]))

        {

            //with json option

            string valstring = new JavaScriptSerializer().Serialize(parameter);

 

            //with comma delimited

            string valstring = string.Join(",", parameter as string[]);

 

            return valstring;

        }

 

        return base.ConvertValueToString(parameter, parameterType);

    }

}

 
 
כעת צריך להגדיר BehaviorExtension המאפשר לנו להתערב בתהליך ה - request, response
 
 

public class CustomHttpBehaviorExtensionElement : BehaviorExtensionElement

{

    protected override object CreateBehavior()

    {

        return new CustomHttpBehavior();

    }

 

    public override Type BehaviorType

    {

        get { return typeof(CustomHttpBehavior); }

    }

}

 
 
הדבר האחרון הוא להגדיר את הקונפיג
 
 

<system.serviceModel>

 

  <services>

    <service name="ServiceImplementation.CRM">

      <endpoint address=""

                binding="webHttpBinding"

                contract="Contracts.ICRM"

                behaviorConfiguration="withWebHttp"></endpoint>

    </service>

  </services>

 

  <behaviors>

    <endpointBehaviors>

      <behavior name="withWebHttp">

        <customWebHttp />

      </behavior>

    </endpointBehaviors>

  </behaviors>

 

  <extensions>

    <behaviorExtensions>

      <add name="customWebHttp"

           type="WCFServiceWebRole1.CustomHttpBehaviorExtensionElement, WCFServiceWebRole1" />

    </behaviorExtensions>

  </extensions>

 

  <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />

</system.serviceModel>

 
 
וכעת נוכל לגלוש בצורה הבאה:
 
או עם json
Posted: Oct 11 2011, 05:54 PM by Shlomo | with no comments
תגים:,

Web Project Development

 

בתפוז עלתה שאלה שקשורה לעבודה עם ה - Development Server, בפוסט זה אני אעבור בקצרה ובהמהירות על האופציות השונות.
 
 
כשעובדים עם פרוייקט web כדי להריץ אותו צריך שרת שיודע להאזין לבקשות ולהחזיר תשובות, כל ההגדרות בנושא נמצאים במאפייני הפרוייקט בטאב Web
 
 
Web properties
 
 
אופציית ברירת המחדל היא עבודה עם השרת הפנימי של Visual Studio, וזהו החלון הצהוב הקופץ כל פעם מצד ימין בשורת המשימות
 
 
VS Server
 
 
כברירת מחדל הוא מקבל כל פעם פורט רנדומלי אחר, במידה ועובדים עם השרת הזה עדיף תמיד לשנות אותו ל - Specific Port ולהגדיר אחד כזה.
 
 
האופצייה המועדפת עלי, היא לסמן את Use Local IIS Web Server, אני מעדיף את שיטת העבודה הזו מכיוון שזה מדמה את המצב האמיתי (בלי לגלות אחרי זה באגים מוזרים כתוצאה מכך שכשהמוצר עולה לאוייר הוא רץ על IIS)
חסרון אחד בשיטה זו, שאי אפשר לסמן Enable Edit and Continue (בתחתית המסך - לא נכנס בתמונה) המאפשר לשנות קוד תוך כדי ריצה.
כמובן שבשיטה זו צריך לדעת לקנפג את IIS (גישה ל - DB ועוד)
 
 
ניתן גם להתקין את IIS Express שהוא גרסה רזה של IIS מצד אחד ומצד שני מכיל את רוב התכונות של IIS 7.5, במקרה זה תקבלו אופצייה של Use IIS Express. (בפוסט אחר אני ארחיב יותר על IIS Express)
Posted: Oct 10 2011, 09:18 AM by Shlomo | with 2 comment(s)
תגים:, ,

List with main item

 

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

public interface IItemWithMain

{

    bool IsMain { get; set; }

}

 

public class ListWithMain<T> : List<T> where T : IItemWithMain

{

    public T MainItem

    {

        get

        {

            return this.FirstOrDefault(x => x.IsMain);

        }

        set

        {

            var mainItem = MainItem;

            if (mainItem != null)

            {

                mainItem.IsMain = false;

            }

 

            value.IsMain = true;

        }

    }

}

 

public class Person : IItemWithMain

{

    public int Id { get; set; }

    public bool IsMain { get; set; }

}

 
 
כעת השימוש בקוד הזה יהיה פשוט ביותר
 

static void Main(string[] args)

{

    ListWithMain<Person> list = new ListWithMain<Person>();

    list.Add(new Person() { Id = 1 });

    list.Add(new Person() { Id = 2, IsMain = true });

    list.Add(new Person() { Id = 3 });

 

    Person p = list.MainItem;

    list.MainItem = list.First(x => x.Id == 1);

}

 
אפשר לראות שאפשר בקלות להוסיף אלמנטים לרשימה, לקבל את הרכיב שמוגדר כראשי, ולשנות את הלאמנט הראשי.
Posted: Oct 09 2011, 10:51 AM by Shlomo | with no comments
תגים:,

יצירת Controller עם מתודות המכילות Actoin Name משותף

 

(פוסט זה נכתב בעזרת חיים בריקמן - אחד האנשים המוכשרים ביותר שאני מכיר)
 
 
נניח שאתם רוצים לכתוב Controller עבור Orders המכיל מתודות לשליפה ועדכון נתונים, והדרישה היא שהפנייה תהיה עם אותו Action Name וה - Controller יזהה את המתודה המתאימה לפי סוג הפנייה (GET, POST, PUT, DELETE)
 
נקח לדוגמא את הקוד הבא:
 

// SELECT

[HttpGet]

public ActionResult Index(int id)

{

    return Content("GET /Orders/{id} | Orders[id]");

}

 

// INSERT

[HttpPost]

public ActionResult Index(OrderDTO order)

{

    return Content("POST /Orders | Insert new Order [" + order.Name + " ] to Orders collection");

}

 

// UPDATE

[HttpPut]

public ActionResult Index(OrderDTO order)

{

    return Content("PUT /Orders | Updates new Order [" + order.Id + " ] in Orders collection");

}

 

// DELETE

[HttpDelete]

public ActionResult Index(int id)

{

    return Content("DELETE /Orders | delete [id] from Orders collection");

}

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

[ActionName("Index")]

כעת מצד הלקוח ניתן יהיה לפנות לשירותים בצורה הבאה:
 
 
$.ajax({url: '/Orders/3', type: 'PUT', data: {Id:2,Name:'asd'}});
 
$.ajax({url: '/Orders/2', type: 'POST', data: "{Id:2,Name:'asd'}"});
 
$.ajax({url: '/Orders/2', type: 'DELETE'});
 
וכדי לקבל מידע ניתן פשוט לגלוש בדפדפן (ששולח GET).
 
 
Posted: Oct 05 2011, 11:14 AM by Shlomo | with no comments
תגים:, , ,

שליחת ערך מסוג enum ל - Controller ב - asp.net mvc

 

לאחרונה התחלתי לעבוד עם asp.net mvc, אני מגלה בו הרבה דברים מעניינים שאכתוב עליהם בתקופה הקרובה, 
 
במשימה האחרונה שלי ניסיתי לכתוב action ב - controller שמקבל כפרמטר ערך עבור enum, הקוד היה נראה כך:
 
בצד ה - controller

public enum MyEnum

{

    A = 0,

    B = 1,

    C = 2

}

 

public ActionResult Save(MyEnum myEnum)

{

    return Json(myEnum);

}

 
בצד ה - view
 

 <input type="button" value="Save" onclick="save()" />

 

<script>

    function save() {

        $.ajax(

            {

                url: '@Url.Action("Save")',

                type: "post",

                data: '{myEnum : 2}',

                contentType: "application/json"

            });

 

    }

</script>

 
 
לרוע המזל כל הזמן קבלתי את השגיאה הבאה:
 
The parameters dictionary contains a null entry for parameter 'myEnum' of non-nullable type 'MvcApplication1.Controllers.MyEnum' for method 'System.Web.Mvc.ActionResult Save(MvcApplication1.Controllers.MyEnum)' in 'MvcApplication1.Controllers.HomeController'. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter.
Parameter name: parameters
 
 
 
 מעיון קצר בשגיאה אפשר להבין שאיכשהו הערך עבור המשתנה myEnum לא הצליח להגיע לשרת.
 
לאחר קצת דיבגגינג (הפעלה של Thrown תחת Debug -> Exception) והורדת הסימון של Enable Just My Code (תחת Tools -> Options -> Debugging)
קבלתי את הודעת השגיאה האמיתית
 
The parameter conversion from type 'System.Int32' to type 'MvcApplication1.Controllers.MyEnum' failed because no type converter can convert between these types.
 
 
זה כבר יותר מעניין, ממתי אי אפשר להמיר בין ערך מספרי (int) לבין enum.
 
 
לאחר חיפוש בגוגל הגעתי למקום כלשהו ברשת (לצערי אני לא מוצא אותו שוב כדי לתת קרדיט) שכתב את דוגמת הקוד הבאה.
 

public class EnumBinder<T> : IModelBinder

{

    private T DefaultValue { get; set; }

 

    #region ctor

    public EnumBinder(T defaultValue)

    {

        DefaultValue = defaultValue;

    }

    #endregion ctor

 

    #region IModelBinder Members

    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)

    {

        return bindingContext.ValueProvider.GetValue(bindingContext.ModelName) == null

                ? DefaultValue :

                GetEnumValue<T>(DefaultValue, bindingContext.ValueProvider.GetValue(bindingContext.ModelName)

                .AttemptedValue);

    }

    #endregion

 

    public static T GetEnumValue<TT>(T defaultValue, string value)

    {

        try

        {

            T enumType = defaultValue;

            enumType = (T)Enum.Parse(typeof(TT), value, true);

            return enumType;

        }

        catch (Exception ex)

        {

            return defaultValue;

        }

    }

 

    public static bool Contains(Type enumType, string value)

    {

        return Enum.GetNames(enumType).Contains(value, StringComparer.OrdinalIgnoreCase);

    }

}

 
 
לא נעבור על הקוד, בגדול הוא יודע לקבל את הערך שנשלח ולהמיר אותו, כל מה שצריך לעשות זה לרשום אותו ב - Application_Strart
 

ModelBinders.Binders.Add(typeof(MyEnum), new EnumBinder<MyEnum>(MyEnum.A));