Showing posts with label C#. Show all posts
Showing posts with label C#. Show all posts

Tuesday, April 21, 2015

Microsoft MVC - Fun with Views

Recently we developed, as part of an overall re-skinning/re-factoring project, a menu service. The purpose of this menu service was to allow all our MVC applications obtain a list of links of other applications that the current user had access. For instance - if the user had a video product we'd provide the link to the DVR manager application. The idea behind this service is that it would be called asynchronously during the application load - after the user had supplied their credentials - and then dynamically adjust the hamburger menu image on the top of the page. Once the service was developed and unit tested I had the opportunity to wire it up to a template project to see how it would be implemented across all our MVC applications.

The Menu Service

The call to the menu service was rather simple. The call was a simple Get to a URL. The pattern of the URL followed this format - https://services.domain.com/MenuService/api/Menu/. Upon success the menu service would return the simple model illustrated below.
    public class MenuItem
    {
        #region Public Properties

        public List Groups { get; set; }

        public string IconImageText { get; set; }

        public string LinkUrl { get; set; }

        public string Name { get; set; }

        #endregion
    }
So as not to get into too much the details here - but basically I'd get a link, an image name that would be displayed on the UI, and a normal name which to display within an A element embedded in a LI element. Upon receipt of the data from the service the following javascript would then be invoked to build the hamburger menu.
     
$.each(data, function (key, value) {
    $("#inlinenavigation").append("
  • " + value.name + "
  • "); });
    Really nothing dramatic or even that exciting here.

    User? What User?

    So you may have noticed the URL pattern above required that it be supplied with a user name - preferably the user name of the person that logged into the site. The question then was so how best to accomplish this? Mind you this call was going to happen on the client in their web browser.

    The first idea was to simply add the user name into the model being passed in the view by the controller. This had some very time consuming implications. This would require that EVERY controller method return a model (we have a few that don't) and that each model come with a "built in" UserName property. Additionally this new property would have to be populated EVERYTIME. And that this property would have to added to each view as a hidden field. Finally every application would have to be retested to make sure the UserName property was populated and that menu service was called properly. This was removed from consideration because of the considerable weight of the code changes and testing that would need to take place.

    The second idea was to create a variable in the ViewBag. This seemed easy enough. With each controller's constructor method (or the constructor of its parent class) fetch the user name. But wait - the constructor doesn't allow the [Authorize] attribute. So maybe move it into the methods that return a view? Sure that might work. However, there are few problems with this approach. First - this would be something that would need to be added on EVERY method call (except the constructor) in the controller. Second - our development follows a specific pattern where the core of the application is developed first (behaviors, models, views, and simply getting it work). Final design tweaks are made my the web designer to ensure compliance to our visual design standards. Finally after the code is reviewed by peers the security layer (Windows Identity Foundation/ADFS) is added into the solution. So you won't be getting the identity claims util you are nearly done - meaning there'll be a lot of code written in each controller (or on its parent) to handle the fact there are no claims. This was also removed as option because every controller object in every application would have to be touched to make this change.

    The final idea was to leverage the Razor engine a bit more than we've normally done. It occurred to us that the changes could occur in one place across all the applications. The beauty behind this approach is that this one place was going to be changed as part of the re-skinning effort anyway. The place to make this change was within the _Layout.cshtml file. Before the @RenderBody() would occur this code was placed in the layout file:
         
            @if (User.Identity.IsAuthenticated)
            {
                // Get the user name from the claims and set it as a hidden input on the page!
                var claimsId = (ClaimsIdentity)User.Identity;
                
            }
            else
            {
                
            }
    
    Basically obtain the user name from the custom claims that is populated after the user has been authenticated. If authentication hasn't occurred send over a default or dummy user account (default user name to be determined as of this writing). Once you have the user name the rest is rather easy - call the menu service upon the document ready, get the links, and add them to the hamburger menu.
         
            // Menu retrieve
            var userName = $("#claims-user-name").val();
    
            $.ajax({
                url: '@WebConfigurationManager.AppSettings.Get("MenuServiceUrl")' + userName,
                type: "GET",
                //crossDomain: true,
                //data: formData,
                success: function (data) {
                    $("#loading").remove();
                    $.each(data, function (key, value) {
                        $("#inlinenavigation").append("
  • " + value.name + "
  • "); }); }, error: function (errorThrown) { // there was an error with the post $("#inlinenavigation").text("ERROR"); } });
    You'll also note there that the Url of the menu service is pulled from the configuration file - this allows different menu services in each environment (Dev, QA, Production) to be invoked

    Sunday, September 29, 2013

    Telerik Kendo Grid - What's the drama behind putting a link a grid's cell

    After a few hours I was about the end of my rope playing with Telerik's Kendo Grid control.  Seriously...how hard could it be to insert a link into grid that will generate an action, e.g. one of the CRUD operations?  Here's how I finally managed this trick.

    The MVC 4 standard approach is rather easy and straight forward.  Really all that is necessary is to post a bunch of Html.ActionLink methods as illustrated in this code snip below.

     @foreach(OfferModel offerModel in ViewData.Model.Offers)  
     {  
       <tr>  
        <td>@Html.DisplayFor(m=> offerModel.Id)</td>  
        <td>@Html.DisplayFor(m=> offerModel.Title)</td>  
        <td>@Html.DisplayFor(m=> offerModel.Description)</td>  
        <td>@Html.DisplayFor(m=> offerModel.Status)</td>  
        <td>  
          @Html.ActionLink("Edit", "Edit", "Offer", new { id=offerModel.Id}, new {@class="edit_button})  
          @Html.ActionLink("Delete", "Delete", "Offer", new { id=offerModel.Id}, new {@class="delete_button})  
          @Html.ActionLink("Copy", "Copy", "Offer", new { id=offerModel.Id}, new {@class="copy_button})  
        </td>  
       </tr>  
     }  
    

    There's a bit more going on here than simply having the action link's place a URL in my table.  I added a class so that these links appear like buttons (standard stuff from jQuery, nothing exciting).

    What you end up with is displayed above when the "buttons" are clicked the appropriate controller method is invoked along with the Id so that the right record is either edited, deleted, or copied.

    Then there's the Kendo grid - which honestly I'm impressed with.  No having to worry about cross browser compatability, build in sort, filter, and a lot more makes it worth the trouble of trying to figure this out.

    There's a lot of postings around putting a link into a Kendo grid.  Some of the ideas presented were pretty decent - sadly they didn't work for me and likely left a few people scratching their heads.  The biggest problem I needed to solve was to be able to embed the "id" of the row in the Html.ActionLink so that the resulting URL would look something like: /Offers/Edit/<id>.

    How I got this work was to insert a client template as shown below.

    @(Html.Kendo().Grid(Model)  
        .Name("OfferModelGrid")  
        .Columns(columns =>  
        {  
          columns.Bound(p => p.Id);  
          columns.Bound(p => p.Title);  
          columns.Bound(p => p.ShortDescription);  
          columns.Bound(p => p.Status);  
          columns.Bound(p => p.StartDate);  
          columns.Bound(p => p.EndDate);  
       
          columns.Bound(p => p.Id)  
            .Filterable(false)  
            .Title("Action")  
            .Template(@<text></text>)  
            .ClientTemplate(Html.ActionLink("Edit", "Edit", "Offer", new {id = "#=Id#"}, new {@class = "edit_button"}).ToHtmlString() +  
                    Html.ActionLink("Delete", "Delete", "Offer", new {id = "#=Id#"}, new {@class = "delete_button"}).ToHtmlString() +  
                    Html.ActionLink("Copy", "Copy", "Offer", new {id = "#=Id#"}, new {@class = "copy_button"}).ToHtmlString());  
        }  
        )  
        .Pageable()  
        .Sortable()  
        .Filterable()  
        .DataSource(datasource => datasource.Ajax().Read(read => read.Action("FetchOffers", "Offer")))  
        )  
    

    A number of examples suggested putting a Html.ActionLine between the <text></text> elements - however this didn't seem to affect much, if anything in the results.  The key piece really is the population of the data that needed to be part of the action's URL - namely the #=Id# you see in the forth parameter.  What this is really doing is taking the Id property of the Model associated with the page and placing it into the resulting action link.  The result is exactly what you need - a link which will invoke the indicated method in the indicated controller.  Add in a bit of class and you end up something that looks remarkably like the original MVC screen.


    Wednesday, May 8, 2013

    Why use System.Runtime.Caching.MemoryCache?

    I ran across a problem this week while putting in the finishing touches of a data access layer for a series of WCF services.  These services needed to pull information from an IBMi (AS/400) "database" (e.g actually the pre-1980's file system).  I won't go too far into the problem, but the need was I had to cache information regarding what environment and/or library to invoke the program running on the IBMi.  This information I decided was likely best stored in a type Dictionary<string, string> - as I could look up the name of the program and easily retrieve the library that I needed be in to invoke the program.

    So then, how best to cache this information between successive calls to a WCF service?

    Well, as it turns out the IIS worker process in version 5.1 and greater is quite handy.  So long as the application pool isn't recycled either from a time-out or by manually recycling the pool the object you define as your ServiceContract will remain in memory.  Meaning any object bearing the static definition within this object will remain, well, static.

    So my first thought on solving this problem was to simply define a Dictionary<string, string> object as a private static member of my ServiceContract class, load it up, and then use it within any object that my ServiceContract class needs into order to its work.  Basically something like this.

     public class Service1 : IService1  
     {  
       private static Dictionary<string, string> myDictionary = new Dictionary<string, string>();  
       
       public string GetData(int value)  
       {  
        Service1.myDictionary.Add(myDictionary.Count.ToString(), value.ToString());  
        return string.Format("You entered: {0}", value);  
       }  
     }  
    

    See any problems yet?

    If your guess was that in order to use the static "myDictionary" object I would need to carry the object to each and every object that requires access to the list of the items within the dictionary then you can immediately see my folly.  If you aren't planning on a having many (or any) helper objects then this really isn't a big deal.  The problem I faced was that the ServiceContract object  of the WCF services I was coding for was about 8 or 9 layers above my data access layer.  I was really going to be popular changing all the objects between the top most ServiceContract and my object - especially since ALL of the objects didn't really care about or need the contents of the Dictionary class.

    Enter the MemoryCache

    MemoryCache is an object located in the System.Runtime.Caching assembly.  This object was added a few years ago with the release of .NET 4.0.   It never really caught my attention until this problem came up - but essential it is a way to store away any objects you might need someplace else.  Or put another way, if you are an old C programmer (like me), it is a way you can tuck away global variables (or objects in this case) for reuse in other places in your application.  The API for this object can be found here.  Also a quick search on this object will provide some information and other sample code on its use.  The purpose here really isn't to describe the API but rather to provide a practical example of its use.

    So on we go with my first demo project that began using the MemoryCache object.  My first set of changes really just swapped out the Dictionary object with the MemoryCache object.  While I didn't solve my problem identified above, this step provided me with some understand of how this object worked as I was successful in creating, storing, retrieving, and updating the Dictionary object with successive calls to my test WCF service.  Below outlines the changes I made to my "GetData" method.  

     public class Service1 : IService1  
     {  
       private static ObjectCache myCache = MemoryCache.Default;  
       
       public string GetData(int value)  
       {  
        Dictionary<string, string> myDictionary = (Dictionary<string, string>)Service1.myCache.Get("ALIST");  
       
        if ( myDictionary==null )  
        {  
          CacheItemPolicy policy = new CacheItemPolicy();  
          policy.Priority = CacheItePolicyPriority.Default;  
          myDictionary = new Dictionary<string,string>();  
          Service1.myCache.Set("ALIST", myDictionary, policy);  
        }  
       
        myDictionary.Add(myDictionary.Count.ToString(), value.ToString());  
       
        return string.Format("OK");  
       }  
     }  
    

    I also added a new method that retrieved a complete list of Dictionary items to the client so I could keep checking if the MemoryCache (and its embedded Dictionary object) would retain the information I needed to keep around.

     public List<Avalue> GetAllCalls()  
     {  
       List<Avalue> values = new List<Avalue>();  
       
       Dictionary<string, string> myDictionary = (Dictionary<string, string>)Service1.myCache.Get("ALIST");  
       
       if ( myDictionary != null)  
       {  
        foreach(var pair in myDictionary)  
        {  
          Avalue aValue = new Avalue() { CallTime = pair.Key, CallValue = pari.Value };  
          values.Add(aValue);  
        }  
       }  
       
       return values;  
     }  
    

    So once this second version of my prototype was working I went to see if another object, which would be put in and out scope during another method call of my service, could get a handle to the same Dictionary object and update it for me.  If this could work then I could avoid having to change all the objects between my data access layer and the ServiceContract object.

    In order to test this I created another object within the WCF project called "DoSomething"  which is defined below.

     public class DoSomething  
     {  
       private ObjectCache doSomethingCache;  
       
       public DoSomething()  
       {  
        doSomethingCache = MemoryCache.Default;  
       }  
       
       public void DoIt(int value)  
       {  
        Dictionary<string, string> myDictionary = (Dictionary<string, string>)doSomethingCache.Get("ALIST");  
       
        if ( myDictionary!=null )  
        {  
          myDictionary.Add("DoSomething " + myDictionary.Count.ToString(), "DoIt" + value.ToString());  
        }  
       }  
     }  
    

    You'll notice that within the constructor of this object it is obtaining a handle to the MemoryCache object.  And then it uses this handle, within the DoIt method, to pull out a Dictionary object where it then adds an entry.  You'll also notice that the handle to the MemoryCache goes out scope along with lifespan of this object.

    The final version of my service adds a new method called SetNewItem.  This method creates a DoSomething object, calls the DoIt method and returns, causing the DoSomething object to go out of scope and eventually get picked for garbage collections.

     public string SetNewItem(int value)  
     {  
       DoSomething something = new DoSomething();  
       
       something.DoIt(value);  
       
       return ( "OK");  
     }  
    

    You then see that I didn't need to pass around the Dictionary or the MemoryCache object and that the DoSomething object appears to be capable of obtaining a handle of Dictionary object.

    So, the question is did it actually work?  Did the MemoryCache manage to stay around between calls?  Was the Dictionary object populated correctly?  The answer if is course yes.  Below is a screen shot of the WinForms application which calls the Service1 web service.  I invoked the GetData method a few times as well as the SetNewItem method.  Each of these calls were given a random number between 0 and 42 by the client.  After calling these methods a number of times I requested a full list of the contents of the Dictionary object to display within a list box on the screen.


    As illustrated above you see can that the Dictionary object is alive and providing me a cached list of items that I can pull from pretty much any place within my running process.  Once the application pool was recycled I pulled down the list again.  However, because the pool was recycled there were no more entries in the Dictionary.