Raven DB

I’ll probably start a series of posts related to RavenDB, since I’m learning to use it. And here is the first one.

I’ve been using SQL queries most of my programmers life and I’m so used to ask database things like this: show me all projects that user has access to.

Or Select * from project where ProjectId in (select ProjectId from UserPermissions where UserId=1)

When you try to use the same logic in RavenDB, it throws you NotSupportedException and no hints of how to do it. Luckily, RavenDb is popular enough to have a good hit score in Google and I managed to find the answer on the interwebs pretty quick.

Here is the way to search in a subset:

#!csharp
var locations = ravenSession
    .Query<Location>()
    .Where(l => l.ProjectId.In<string>(currentUser.ProjectIds))
    .ToList();

And you’ll have to add using Raven.Client.Linq; to your class, so In<> becomes available.

Remove Server http header from ASP.NET MVC Application

There is an opinion in the online community that HTTP Headers in your application must not be giving out information about your site. But in every StackOverflow question about removing headers, there always will be someone saying “why bother?”. Well.. don’t bother if you don’t want to. You are quite right, there are ways to detect what server and technologies are used to serve the site. But I do not know any of these techniques, so I’ll keep removing headers from my applications.

Also it seemes that IIS insists on providing Server header with every request and there is no way to remove it via web.config. To get rid of this header I have seen people going out of their way with HTTP Modules and UrlScan. But in MVC4 (not sure about version 3 – never tried it there) and IIS 7.5 you can easily do that in you Glabal.asax.cs file.

In the application request life-cycle there is EndRequest() event that can do what we want:

    protected void Application_EndRequest()
    {
        // removing excessive headers. They don't need to see this.
        Response.Headers.Remove("Server");
    }

Much the same way you can remove any other header from the reply: X-Powered-By, X-AspNetMvc-Version and etc. But for these there are legit ways to do that through web.config.

Itenso TimePeriod library: change dates for TimePeriod

It took me a while to figure out how to change Start and End dates on an instance of TimePeriod. There are many functions to shrink and expand start and end dates, move the period by a certain time span, but I could not find anything that would just change .Start and .End.
Obviously you can do this:

timePeriod.Start = new DateTime(1999, 7, 9);
timePeriod.End = new DateTime(2012, 7, 9);

But this can lead to an exception if you are trying to move timeperiod to the future: Say start was on 1 Jan 2000, and on 1 Feb 2000. Then you give it .Start = 1 June 2000 and you get an exception saying “date out of range blah-blah”. That is because you are trying make start after end. You can swap assignment around, assign .End first then .Start. But you still can get an exception if you try to move the period to the past.

Instead, use .Setup method:

timePeriod.Setup(new DateTime(1999, 7, 9), new DateTime(2012, 7, 9));

And this should change your dates safely.

Disclaimer: I have not checked the source code, and not sure if that somehow ignores some internal mechanisms. So before blaming me that .Setup does not work, check with the author.

Set Validation error in ASP.Net MVC Controller

Every time I need to set a validation error in a controller, I need to look up how to do this. But it is very simple. So I’ll just write it down here, for future reference: In controller do:

ModelState.AddModelError("FieldName", "Error message for User");

If you leave FieldName blank, the error will come up in validation summary. In a view you’ll have to do this:

@Html.ValidationSummary(true) // for the whole model
@Html.ValidationMessageFor(m => m.UserName) // for individual model field

MVC3: Form submitted only by GET method, without specifying controller and action

Sometimes in ASP.Net MVC you need to submit a form that will always go by GET method. But standard BeginForm() function does not have an override that does not requires specifying Controller and Action where you would like to submit the form back to. And sometimes you don’t want to specify where the form to go to, cause the same form can go into multiple controllers (think of a partial view). And default BeginForm() submits only by POST. So there is no default way to say I want to submit form by POST to wherever the form has came from.

I had to poke through MVC source code to figure out how to do what I want, and keep all the parameters and not mess up any paths. Here you go:

public static MvcForm BeginForm(this HtmlHelper htmlHelper, FormMethod method)
{
    var rd = htmlHelper.ViewContext.HttpContext.Request.RequestContext.RouteData;
    var currentAction = rd.GetRequiredString("action");
    var currentController = rd.GetRequiredString("controller");

    return htmlHelper.BeginForm(currentAction, currentController, rd.Values, method);
}

Read Only User in MVC3 with minimal changes

I had a task of creating a read-only user role in our MVC application. And given the limited time for the task I had to do that with minimal changes to the domain/architecture.

I have started with creating “Readonly” user role. Hopefully I don’t need to explain how to do that.

This is not the most elegant solution to the problem, but it kinda does the job. It block read-only user from using the POST actions, also if available substitutes Edit views with Details views (presuming this is CRUD application). Also we add javascript to Edit pages where we don’t have Details page.

Then I created a global filter:


    public class ReadOnlyFilter : IActionFilter 
    {
        public void OnActionExecuting(ActionExecutingContext filterContext)
        {
            var currentUser = HttpContext.Current.User;
            if (currentUser == null || !currentUser.Identity.IsAuthenticated)
                return; // user is not logged in yet. Give them a break, please! Maybe they just trying to login. Not even touching your precious controllers yet!


            if (!currentUser.IsInRole("Readonly")) 
                return; // user is not read-only. Nothing to see here, move on!


            // Presume User is read-only from now on************************************

            var readOnlyAttribute = GetForReadOnlyUserAttribute(filterContext);

            // If DisableUserInput applied to the action, turn on JS for disabling the input fields on the view
            if (readOnlyAttribute != null && readOnlyAttribute.DisableInputFields)
                DisableInputOnForm(filterContext);

            // if AllowPost Attribute applied to the action, ignore the rest of this filter and get on with this
            if (readOnlyAttribute != null && readOnlyAttribute.AllowPost == true)
                return;

            // if action is of type post - deny
            if (filterContext.HttpContext.Request.HttpMethod.ToLower() == "post" )
            {
                ReadOnlyPage(filterContext);
                return;
            }

            // if action is "Create" - deny access
            if (filterContext.ActionDescriptor.ActionName.ToLower() == "create")
            {
                ReadOnlyPage(filterContext);
                return;
            }

            // if action is edit - check if Details action exits -> redirect to it.
            if (filterContext.ActionDescriptor.ActionName.ToLower() == "edit")
            {
                // find a method named Details that has exactly the same set of parameters as the request.
                var detailsAction = GetActionMethod(filterContext, "Details");

                // if "Details" action exist, we redirect the user into that action.
                if (detailsAction != null)
                {
                    // (in)sanity check. Also Resharper told me this could be null.
                    if (filterContext.HttpContext.Request.Url == null) return;

                    // simply replace the "Edit" with "Details" in a url. This is DIRTY, just the way I like my women: http://goo.gl/wrKuI
                    var currentUrl = filterContext.HttpContext.Request.Url.ToString();
                    var newUrl = currentUrl.Replace("Edit", "Details");

                    // and redirect to the new url.
                    filterContext.Result = new RedirectResult(newUrl);
                }
                else
                {
                    DisableInputOnForm(filterContext);
                }
            }
        }



        /// 
        /// Redirect user to read-only message page
        /// 
        /// 
        private void ReadOnlyPage(ActionExecutingContext filterContext)
        {
            filterContext.Result = new RedirectResult("~/ReadOnlyAccess");
        }


        /// 
        /// Activate javascript to disable all input fields.
        /// 
        private void DisableInputOnForm(ActionExecutingContext filterContext)
        {
            filterContext.Controller.ViewBag.DisableAllInputElements = true;
        }


        /// 
        /// If ForReadOnlyUserAttribute is applied for a method, return that attribute object.
        /// If actionName provided, work with that action in the controller. Otherwise work with currently executed action.
        /// 
        private ForReadOnlyUserAttribute GetForReadOnlyUserAttribute(ActionExecutingContext filterContext, String actionName = null)
        {
            MethodInfo method = GetActionMethod(filterContext, actionName);
            if (method == null) return null;

            var readOnlyAttribute = (ForReadOnlyUserAttribute)Attribute.GetCustomAttribute(method, typeof(ForReadOnlyUserAttribute));

            return readOnlyAttribute;
        }


        /// 
        /// Return MethodInfo for the executed action on the controller.
        /// If actionNameString is provided we return MethodName for the provided name.
        /// If no actionNameString is provided, we return the currently executed Action.
        /// 
        private MethodInfo GetActionMethod(ActionExecutingContext filterContext, String actionNameString = null)
        {
            var controllerType = filterContext.Controller.GetType();

            // get all the parameters from the original request. Copy Types of parameters in an array. 
            var paramTypes = filterContext.ActionParameters
                .Values
                .Where(p => p != null)
                .Select(o => o.GetType())
                .ToArray();

            var actionName = actionNameString ?? filterContext.ActionDescriptor.ActionName;

            // find a method that has exactly the same set of parameters as the request.
            var detailsAction = controllerType.GetMethod(actionName, paramTypes);

            return detailsAction;
        }


        public void OnActionExecuted(ActionExecutedContext filterContext)
        {
            // 1960: "I have a great idea! lets have every person in the country carry a radio tracking beacon!" 
            //  "That'll never fly!"  
            //
            // 2012: "I can has TWO iphones??"
        }
    }

That class is registered in Global.asax.cs:

        public static void RegisterGlobalFilters(GlobalFilterCollection filters)
        {
            filters.Add(new ReadOnlyFilter());
        }

And you need ForReadOnlyUserAttribute.cs:

    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false) ]
    public class ForReadOnlyUserAttribute : Attribute
    {
        public bool DisableInputFields { get; set; }
        public bool AllowPost { get; set; }
    }

And in your global Layout page you need to add following block of code for javascript. Taking you already have jQuery in your project:

    @{  // if we are working with read-only user and Edit page does not have Details alternative, we show Edit page, but with all the controls disabled.
        var disable = (bool?) ViewBag.DisableAllInputElements;  // this is set in ReadOnlyFilter
        if (disable.HasValue && disable.Value == true)
        {
            //script from asawyer: http://stackoverflow.com/a/12477283/809357
            
        }
    }

To disable input forms on the pages, add [ForReadOnlyUser(DisableInputFields = true)] attribute on the controller action:

        [ForReadOnlyUser(DisableInputFields = true)]
        public virtual ViewResult Details(int id)
        {
            var model = _personRepository.Find(id);
            return View(model);
        }

To allow POST actions add [ForReadOnlyUser(AllowPost = true)] to actions where you take POST submit.
Beware – this allows unauthorised users to do POST actions. That was fine in my case, but might not be acceptable for you, so change the filter code a bit.

And that seems to be it.

Generate QR Barcode in ASP.Net MVC

I had a task of creating a QR barcode and displaying it on a web-page from ASP.Net MVC system. There are a lot of free web-services for generating a qr-codes for you, ( like http://qrcode.kaywa.com/ ) But this time I did not want to use a service for various reasons. I had to get the barcode generated on my server. (Just accept it as default!)

I have found many .Net libraries that generate a qr-barcodes, but for some reason I did like QrCode.Net. Probably because it is hosted on codeplex and I keep my open-source projects there as well.

I have downloaded their Dll file, added it as a reference to my MVC project:

In my MVC project I have added a new action that returned a FileStremResult:

using Gma.QrCodeNet.Encoding;
using Gma.QrCodeNet.Encoding.Windows.Controls;

public virtual FileResult BarcodeImage(String barcodeText)
{
    // generating a barcode here. Code is taken from QrCode.Net library
    QrEncoder qrEncoder = new QrEncoder(ErrorCorrectionLevel.H);
    QrCode qrCode = new QrCode();
    qrEncoder.TryEncode(barcodeText, out qrCode);
    Renderer renderer = new Renderer(5, Brushes.Black, Brushes.White);

    // write to file if required for auditing
    //renderer.CreateImageFile(qrCode.Matrix, String.Format(@"d:\tmp\{0}.png", barcodeText), ImageFormat.Png);

    Stream memoryStream = new MemoryStream();
    renderer.WriteToStream(qrCode.Matrix, memoryStream, ImageFormat.Png);

    // very important to reset memory stream to a starting position, otherwise you would get 0 bytes returned
    memoryStream.Position = 0;

    var resultStream = new FileStreamResult(memoryStream, "image/png");
    resultStream.FileDownloadName = String.Format("{0}.png", barcodeText);

    return resultStream;
}

UPDATE 23 Dec 2013: Since this article was written, the API of QrCode.Net has changed slightly and the code above does no longer compile. So see the new code:

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Web.Mvc;
using Gma.QrCodeNet.Encoding;
using Gma.QrCodeNet.Encoding.Windows.Render;

namespace SampleBarCodeMvc.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }

        public ActionResult BarcodeImage(String barcodeText)
        {
            // generating a barcode here. Code is taken from QrCode.Net library
            QrEncoder qrEncoder = new QrEncoder(ErrorCorrectionLevel.H);
            QrCode qrCode = new QrCode();
            qrEncoder.TryEncode(barcodeText, out qrCode);
            GraphicsRenderer renderer = new GraphicsRenderer(new FixedModuleSize(4, QuietZoneModules.Four), Brushes.Black, Brushes.White);

            Stream memoryStream = new MemoryStream();
            renderer.WriteToStream(qrCode.Matrix, ImageFormat.Png, memoryStream);

            // very important to reset memory stream to a starting position, otherwise you would get 0 bytes returned
            memoryStream.Position = 0;

            var resultStream = new FileStreamResult(memoryStream, "image/png");
            resultStream.FileDownloadName = String.Format("{0}.png", barcodeText);

            return resultStream;
        }
    }
}

Then in one of the views I place this image tag:

  <img src="/Controller/BarcodeImage?barcodeText=Hello"/>

Where Controller is a name of your controller where you placed the action. And that will give you an image with a barcode and word “Hello” encoded in it.

I have quickly created a sample solution in Visual Studio 2013 (you should be able to open that in VS2012):
SampleBarCodeMvc – this is to show a working example.

Enjoy!

Update: QrCode.Net is now available via nuget package and can be installed either via Nuget inside of your Visual Studio or in VS-console: Install-Package QrCode.Net

ASP.Net MVC: Autosave drop-down with notification

I did spend quite a time figuring out jQuery verbs to do just what I needed. So I’ll share this with my blog and possible readers. On my view I had a model written out, some other stuff, but here I’m only interested in DropDown and JavaScript bits:

@Html.DropDownListFor(m => model.tagId, new SelectList(ViewBag.Tags, "Value", "Text", model.tagId), new {@class = "document-category", data_id = model.modelId})

where

ViewBag.Tags was populated in controller like this:

ViewBag.Tags = _tagRepository.All
    .OrderBy(t => t.Name)
    .Select(t => new { Text = t.Name, Value = t.TagId.ToString() })
    .OrderBy(t => t.Text)
    .ToList();

Back to the view. Added JavaScript like this one:

$(document).on("change", ".document-category", function () {
    var select = $(this);
    $.ajax({
        type: "POST",
        url: "@(Url.Action("Action", "Controller"))",
        data: { id: select.data("id"), 
        tagId: $("option:selected", select).val()},
        error: function () {
            select.after("Error occurred");
        },
        success: function (data) {
            if (data.Success === true) {
                select.after(function () {
                    return $('<div> Saved </div>').delay(1000).fadeOut(1000);
                });
            } else {
                select.after("Error occurred");
            }
        }
    });
});

The very first line can be replaced by $(".document-category").change(function(){ but this time I had to deal with other java-script library that was stripping-bare my stuff. On the other end of the ajax request you need to put controller action:

[HttpPost]
public virtual JsonResult Action(int id, int? tagId)
{
        //blah, do some update stuff
    return Json(new {Success = true, Message = "Updated"});
}

Convert Due Date to human readable format

Sometimes you need to show date in a “Due in X-days” format, not just a date. This will convert date to a string with granularity: Today, tomorrow, yesterday, days, weeks, months.

public static class HumanTime
{
    public static string DueDate(DateTime dueDate) {
        return DueDate(dueDate, DateTime.Now);
    }

    public static string DueDate(DateTime dueDate, DateTime dateNow) {
        DateTime nowDate = dateNow;

        TimeSpan ts = nowDate - dueDate;

        if (dueDate.Date == DateTime.Today)
        {
            return "due today";
        }

        if (dueDate.Date == DateTime.Now.AddDays(1).Date)
        {
            return "due tomorrow";
        }

        if (dueDate.Date == DateTime.Now.AddDays(-1).Date)
        {
            return "was due yesterday";
        }


        int totalDays = (int)Math.Round(ts.TotalDays);
        if (Math.Abs(totalDays) &lt; 7)
        {
            return DueStringFormat(totalDays, "day");
        }

        int weeks = (int)Math.Round( ts.TotalDays / 7 );
        if (Math.Abs(weeks) &lt; 5 && Math.Abs(totalDays)&lt;30 )
        {
            return DueStringFormat(weeks, "week");
        }

        int months = (int)Math.Round( ts.TotalDays / 30 );
        return DueStringFormat(months, "month");
    }


    public static string DueStringFormat(int count, string unit)
    {
        string format = "{0} {1}";
        if (count &lt; 0)
        {
            format = "due in {0} {1}";
        } 
        else
        {
            format = "{0} {1} overdue";
        }

        return String.Format(format, Math.Abs(count), unit.Quantify(count));
    }


    /// Adds "s" suffix to a word if there are more than one thing involved.
    public static string Quantify(this String singular, int count)
    {
        int abs = Math.Abs(count);
        if (abs > 1)
            return singular+"s";
        else 
            return singular;
    }
}

Enums and Display attribute

Enums are handy. But they do not allow you have easy Text description on them.
How about using DisplayAttribuite on enum values and have that text in a drop-down? Easy!

/// <summary>
/// Creates a SelectList from Enum, taking Description values from Enum fields
/// Taken from here: http://stackoverflow.com/a/3705387/809357
/// </summary>
public static SelectList ToSelectListWithDisplayName<T>(this T enumeration, string selected = "")
{
    var source = Enum.GetValues(typeof(T));

    var items = new Dictionary<object, string>();

    var displayAttributeType = typeof(DisplayAttribute);

    foreach (var value in source)
    {
        FieldInfo field = value.GetType().GetField(value.ToString());

                    if (field == null) continue;

        DisplayAttribute attrs = (DisplayAttribute)field.GetCustomAttributes(displayAttributeType, false).FirstOrDefault();
        if (attrs != null)
        {
            items.Add((int)value, attrs.GetName());
        }else   // in case Description attribute is not available, we fall back to the default name
        {
            items.Add((int)value, value.ToString());
        }
    }
    return new SelectList(items, "Key", "Value", selected);
}

Also you can get just on Display Name for one Enum Value:

public static string GetDisplayName(this Enum value)
{
    FieldInfo field = value.GetType().GetField(value.ToString());

    if (field == null)
        return String.Empty;

    object[] attribs = field.GetCustomAttributes(typeof(DisplayAttribute), true);
    if(attribs.Length > 0)
    {
        return ((DisplayAttribute)attribs[0]).GetName();
    }
    return value.ToString();
}